标签:return 数据 erase 过程 get ++i lang 去除 并查集
本题链接:CF982 D. Shark
给定\(n\)个数,定义一个分度值\(k\):将数组中小于\(k\)的连续段找出来,要求每段的长度都相等,在此前提下找出能让连续小于分度值\(k\)的段数最多的\(k\),如果还有多解,则输出最小的\(k\).
数据范围:
\(1 \leq n \leq 10^5\)
\(1 \leq a_i \leq 10^9\)
所有的\(a_i\)互不相同
联系数据范围不难想到有可能是一个枚举\(k\)的值再做check其是否合理以及对应有多少段数的过程.很自然的考虑二分,但是没有关于\(k\)本身取值大小的单调性,做不了二分.
虽然没有单调性,但是值得注意的是,每个可能的\(k\)应该是某个\(a_i+1\),恰好把某个\(a_i\)放入连续的段落中,且\(k\)此时是最小值.所以可以枚举每个\(a_i\),当前的\(k\)就是\(a_i+1\).因为想要求的是\(k\)的最小值,所以不妨先把所有\(\{a\}\)排序,之后按顺序枚举.那么情况也比较显然了:现在就是考虑如果已经做完了\(1 - a_{i-1}\)的问题,再加入\(a_i\)之后怎么求出他对上一个问题的结果的影响.由于所有的\(a_i\)互不相同,所以每次枚举到一个新的\(a_i\)事实上也只会把\(a_i\)加入到这个问题中,不会再有更多的数了.把所有小于分度值\(k\)的段落链接起来,那么加入\(a_i\)之后要么是链接左右两个,要么是只链接了左边和右边的某一个,要么是新开的一个段落,可以注意到段落的长度是只会上升的,而且也不会出现断开某些段落的情况,几种情况分别去讨论就可以了.
考虑具体要维护什么东西:第一个是连续段落的连通性,这个用并查集维护一下每个点所属于的段落的起始点的下标就可以了.其次要知道每个段落的长度,并查集带权即可.还有一个就是要知道当前所有段落的长度各有多少个,只有当所有段落的长度相同的时候,才满足题目的条件,此时需要求段数,很显然就是一共有多少个数去除段的长度,更新一下最多段数对应的最小的\(k\)就可以了.
这个题写起来有一点麻烦的点是需要分清楚原有数组和排序后数组的关系,也没有那么难.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
typedef pair<int,int> pii;
#define x first
#define y second
const int N = 1e5+7;
pii a[N];
int fa[N],siz[N],b[N];
map<int,int> cnt;
int find(int x)
{
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
inline void del(int x)
{
if(--cnt[x] == 0) cnt.erase(x);
}
int main()
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d",&a[i].x),a[i].y = i,fa[i] = i,siz[i] = 1;
forn(i,1,n) b[i] = a[i].x;
sort(a + 1,a + n + 1);
int resk = 0,resv = 0;
forn(i,1,n)
{
bool lf = (a[i].y > 1 && b[a[i].y - 1] < b[a[i].y] + 1),
rt = (a[i].y < n && b[a[i].y + 1] < b[a[i].y] + 1);
if(lf && rt)
{
int x = find(a[i].y - 1),y = find(a[i].y + 1);
del(siz[x]);del(siz[y]);
siz[x] += siz[y] + 1;
fa[a[i].y] = x,fa[y] = x;
++cnt[siz[x]];
if(cnt.size() == 1 && resv < i / siz[x]) resk = a[i].x + 1,resv = i / siz[x];
}
else if(lf)
{
int x = find(a[i].y - 1);
del(siz[x]);
siz[x] += 1;
fa[a[i].y] = x;
++cnt[siz[x]];
if(cnt.size() == 1 && resv < i / siz[x]) resk = a[i].x + 1,resv = i / siz[x];
}
else if(rt)
{
int y = find(a[i].y + 1);
del(siz[y]);
siz[a[i].y] = siz[y] + 1;
fa[y] = a[i].y;
++cnt[siz[a[i].y]];
if(cnt.size() == 1 && resv < i / siz[a[i].y]) resk = a[i].x + 1,resv = i / siz[a[i].y];
}
else
{
++cnt[siz[a[i].y]];
if(cnt.size() == 1 && resv < i / siz[a[i].y]) resk = a[i].x + 1,resv = i / siz[a[i].y];
}
}
printf("%d",resk);
return 0;
}
标签:return 数据 erase 过程 get ++i lang 去除 并查集
原文地址:https://www.cnblogs.com/HotPants/p/14398296.html