CF1446D2 Frequency Problem


题面

给出 \(n(1≤n≤200000)\) 个元素组成的序列 \(a_1,a_2,...,a_n\)

求最长的子段使得其中有至少两个出现次数最多的元素。

输出最长子段长度。

题解

看到了没啥思路

让求的是满足众数个数 >1 的最长区间长度,然后这题有一个关于众数的性质,就是答案区间的众数一定包含序列的众数 (记为 k )

因为若存在出现次数比 k 大的数,可以延伸区间直到其出现次数 <= k的次数

然后这场的 D1 值域是很小的,就可以枚举答案区间另一个众数,然后求出两个数出现次数相同的最大区间

当值域变大之后,考虑根号分治,对出现次数分治,若全局出现次数 > T,就按 D1 做

否则枚举在答案区间 k 的出现次数,用双指针尺取法就行了,需要满足区间内存在一个出现次数不小于 k 的数

代码

#include
using namespace std;
#define il inline
const int T=50;
const int N=2e5+11;
int n,mx,ans,num;
int a[N];
int mn[N<<1|1];
int cnt[N],cnx[N];
il int max_(int x,int y){return x>y?x:y;}
il int read(){
	int s=0;char ch=getchar();
	while(ch>'9'||ch<'0')ch=getchar();
	while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s;
}
void add(int i){
	if(i==n+1)return;
	int x=a[i];++cnt[x];if(x==mx)return;
	++cnx[cnt[x]];if(cnt[x]!=1)--cnx[cnt[x]-1];
	if(cnt[x]>num)num=cnt[x];
}
void pop(int i){
	if(i==n+1)return;
	int x=a[i];--cnt[x];if(x==mx)return;
	--cnx[cnt[x]+1];if(cnt[x])++cnx[cnt[x]];
	if(cnt[x]+1==num&&!cnx[cnt[x]+1])num=cnt[x];
}
void solve2(int x){
	if(x>n)return;
	num=0;int l=1,r=0;
	memset(cnt,0,sizeof(cnt));
	memset(cnx,0,sizeof(cnx));
	for(;l<=n&&r<=n;pop(l++)){
		while(r<=n&&cnt[mx]<=x)add(++r);pop(r--);
		if(num>=x)ans=max_(ans,r-l+1);
	}
}
void solve1(int x){
	if(x==mx)return;
	memset(mn,-1,sizeof(mn));
	mn[N]=0;for(int ct1=0,ct2=0,k,i=1;i<=n;++i){
		ct1+=a[i]==mx;
		ct2+=a[i]==x;k=ct1-ct2;
		if(mn[k+N]!=-1)ans=max_(ans,i-mn[k+N]);
		else mn[k+N]=i;
	}
}
signed main(){
	n=read();for(int i=1;i<=n;++i){
		++cnt[a[i]=read()];
		if(cnt[a[i]]>cnt[mx])mx=a[i];
	}for(int i=1;i<=n;++i)if(cnt[i]>=T)solve1(i);
	for(int i=1;i

相关