CF1446D2 Frequency Problem (Hard Version)(根号分治)
首先这道题有一个结论:这两个元素当中一定有一个是众数,证明略。
那么考虑对于出现次数大于等于 (sqrt{n}) 的数,我们可以把这些数枚举一下,然后这样做:
把值为当前数的位置标为 1 ,把值为众数的标为 -1 ,其他的都是 0 ,然后做一遍前缀和,记录每一个前缀和值的出现的最早的位置。
于是区间的长度那就是当前位置减掉这个前缀和第一次出现的位置(这样的话这个区间和就是 0)。
这部分的数的个数不超过 (sqrt{n}) 个。
接下来是对于出现次数小于 $sqrt{n} $的数。
我们可以枚举区间当中出现次数最多的数的次数,再枚举每一个 (r) ,求出最小的左端点 (L_r) ,整个过程是一个双指针。
可以参考这片题解
代码:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;bool f=false;char ch=getchar();
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
const int N=2e5+5,T=505,INF=1e9+7;
int n,Maxn,cnt,Ans,S,val;
int a[N],Num[N],sum[N],t[N*2],cnt1[N],cnt2[N];
vector<int>v;
inline void Modify(int x,int v){cnt2[cnt1[x]]--,cnt1[x]+=v,cnt2[cnt1[x]]++;}
int main(){
read(n),S=sqrt(n);
for(int i=1;i<=n;i++) read(a[i]),Num[a[i]]++;
for(int i=1;i<=n;i++){
if(Num[i]==Maxn) cnt++;
if(Num[i]>Maxn) cnt=1,Maxn=Num[i];
}
if(cnt>1){write(n);return 0;}
for(int i=1;i<=n;i++){
if(Num[i]==Maxn) val=i;
else if(Num[i]>S) v.push_back(i);
}
for(int i=0;i<v.size();i++){
int k=v[i];
for(int j=1;j<=n;j++) sum[j]=sum[j-1]+(a[j]==k? -1:(a[j]==val? 1:0));
for(int j=-n;j<=n;j++) t[n+j]=INF;
for(int j=1;j<=n;j++) Ans=max(Ans,j-t[n+sum[j]]+1),t[n+sum[j-1]]=min(t[n+sum[j-1]],j);
}
for(int i=1;i<=S;i++){
for(int j=1;j<=n;j++) cnt1[j]=cnt2[j]=0;
int l=1,r=0;
while(r<n){
r++,Modify(a[r],1);
while(cnt1[a[r]]>i) Modify(a[l],-1),l++;
if(cnt2[i]>=2) Ans=max(Ans,r-l+1);
}
}
write(Ans);
return 0;
}