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