Codeforces 1446D2

Codeforces 题面传送门 & 洛谷题面传送门

人菜结论题做不动/kk

首先考虑此题一个非常关键的结论:我们设整个数列的众数为 (G),那么在最优子段中,(G) 一定是该子段的众数之一。考虑反证法,如果最优子段中众数出现次数 (<) 该子段中出现次数最多的数的出现次数,那么我们考虑向左向右扩展这个区间,显然由于 (G) 是整个区间中出现次数最多的数,我们总可以找到一个时刻,满足 (G) 的出现次数 (ge) 原子段中出现次数最多的数的出现次数,此时子段的长度肯定不劣于原子段的长度,答案也不会变得更差。

看出这个性质之后 D1 就变得异常 trivial 了,考虑枚举除了 (G) 之外字段中另一个众数 (x),那么考虑一个非常常用的套路:将序列中 (G) 的权值看作 (1)(x) 的权值看作 (-1),那么一个子段满足条件,当且仅当该子段中所有数的权值之和恰好为 (0),拿个桶记录一下即可。

接下来考虑怎样解 D2。注意到这个东西长得像极了 P4062 Code+#1 Yazid 的新生舞会,而这东西用线段树显然是不太好维护的,因此考虑我的那个根分做法。考虑将所有数分为出现次数 (lesqrt{n})(>sqrt{n}) 进行处理。出现次数 (>sqrt{n}) 的数的个数显然是 (mathcal O(sqrt{n})) 级别的,我们对于这些数重复一遍 D1 的过程即可。对于出现次数 (lesqrt{n}) 的数我们换个角度批量处理这些数,即我们不关心出现次数最多的是哪个数,我们直接枚举众数的出现次数 (c),然后双针,对于每个左端点,向右一直扩展直到其中出现次数最多的数出现次数 (>c),如果我们发现出现次数最多的数不唯一则更新答案即可。这样复杂度就是 (nsqrt{n})

const int MAXN=2e5;
const int DLT=MAXN+3;
int n,a[MAXN+5],cnt[MAXN+5],fst[DLT<<1],cnt_cnt[MAXN+5],res=0;
int main(){
	scanf("%d",&n);int G=0,mxcnt=0,lim=(int)sqrt(n),mx=0;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),cnt[a[i]]++;
	for(int i=1;i<=n;i++) chkmax(mx,cnt[i]);
	for(int i=1;i<=n;i++) if(cnt[i]==mx) mxcnt++,G=i;
	if(mxcnt>=2) return printf("%d
",n),0;
//	printf("%d
",G);
	for(int i=1;i<=n;i++) if(cnt[i]>lim&&i!=G){
		memset(fst,-1,sizeof(fst));fst[DLT]=0;
		for(int j=1,s=0;j<=n;j++){
			if(a[j]==G) s++;if(a[j]==i) s--;
			if(~fst[s+DLT]) chkmax(res,j-fst[s+DLT]);
			else fst[s+DLT]=j;
		}
	} //printf("%d
",res);
	for(int i=1;i<=lim+3;i++){
		memset(cnt,0,sizeof(cnt));int mx=0;
		memset(cnt_cnt,0,sizeof(cnt_cnt));
		for(int l=1,r=1;l<=n;l++){
			while(r<=n&&max(mx,cnt[a[r]]+1)<=i){
				cnt_cnt[cnt[a[r]]]--;cnt[a[r]]++;cnt_cnt[cnt[a[r]]]++;
				chkmax(mx,cnt[a[r]]);++r;
			} //printf("%d %d %d
",i,l,r);
			if(cnt_cnt[mx]>=2) chkmax(res,r-l);
			cnt_cnt[cnt[a[l]]]--;cnt[a[l]]--;cnt_cnt[cnt[a[l]]]++;
			if(!cnt_cnt[mx]) mx--;
		}
	} printf("%d
",res);
	return 0;
}
原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-1446D2.html