【CF1446D2】Frequency Problem(线性做法)
题意
给定 \(n\) 和序列 \(a_1,a_2,\cdots,a_n\),求众数不唯一的区间的最大长度。众数定义:出现次数最多的数。
\(n\le 2\times10^5\)。
分析
首先 \(O(n \max(a_i))\) 做法可以看这篇,本题解继承该做法。
设全局众数为 +
,枚举的数为 -
。相当于每次在全是 +
的序列中插入几个 -
,然后要找到最长的 +-
个数相同的区间。我们想让复杂度只和 -
的个数有关。
观察到对于一个 +-
个数相同的串,一定能产生 +
和 -
之间的完美匹配,使得匹配边不相交(原理和括号匹配差不多)。因此,我们对每个 -
分别找到它向前、向后匹配的第一个 +
把它标记成有用的。那么未被标记的 +
一定不会被合法区间经过,且有用的 +
个数和 -
的个数同级。
实现的时候要对所有数一起做。重新设众数为 \(x\),其余数为 \(y\),先只考虑 \(y\) 在 \(x\) 左边的情况。维护一个栈存二元组 \((y,y_c)\),表示前面 \(y_c\) 个 \(y\) 等待匹配。扫到 \(x\) 时就把栈扫一遍(顺便把 \(y_c=0\) 的清掉),扫到 \(y\) 时就令 \(y_c\gets y_c+1\)(顺便可能丢到栈里)。正反各做一遍就得到若干个 \((i,y)\) 表示 \(x_i\) 对 \(y\) 有用。
预处理完后,只要找到所有 \((i,y),(i+1,y),\cdots,(j,y)\) 的连续段,再把位于 \(x_{i-1}\) 和 \(x_{j+1}\) 之间的 \(y\) 的位置拿出来归并排序得到 +-
序列,直接暴力做。序列总长不超过 \(3\sum y_c\) 所以复杂度是线性。
对 \((i,y)\) 用基数排序才是真的线性,不过直接用 sort 比较方便。
分析
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define gc() (ib==ie&&(ie=(ib=in)+fread(in,1,T,stdin),ib==ie)?-1:*ib++)
using namespace std;
const int T=1<<20,maxn=2e5+5;
char in[T],*ib=in,*ie=in;
int n,m,x,a[maxn],cnt[maxn],sum[maxn],b[maxn],id[maxn],pos[maxn],lft[maxn],s[maxn],mn[maxn],ans;
struct sth{
int c,d;
bool operator==(const sth&o)const{return c==o.c&&d==o.d;};
}e[maxn*2];
int rd(int x=0,char ch=0){
do ch=gc();while(ch<48||ch>57);
do x=10*x+(ch&15),ch=gc();while(ch>=48&&ch<=57);
return x;
}
void prework(int t){
memset(lft,0,sizeof(lft)),s[0]=0;
for(int i=t==1?1:n;i>=1&&i<=n;i+=t){
if(a[i]!=x){
if(!lft[a[i]]++)s[++s[0]]=a[i];
}else{
per(j,s[0],1){
e[++m]={s[j],id[i]};
if(!--lft[s[j]])swap(s[j],s[s[0]]),s[0]--;
}
}
}
}
void solve(int l,int r,int a[],int n,int b[],int m){
memset(mn,-1,sizeof(mn[0])*(n+m+1));
int cur=m;
mn[cur]=l;
for(int i=1,j=1;i<=n||j<=m;){
int t,x;
if(j>m||(i<=n&&a[i]<b[j]))t=1,x=a[i++];
else t=-1,x=b[j++];
if(~mn[cur])ans=max(ans,x-mn[cur]-1);
if(mn[cur+=t]==-1)mn[cur]=x;
}
if(~mn[cur])ans=max(ans,r-mn[cur]-1);
}
int main(){
n=rd();
rep(i,1,n)++cnt[a[i]=rd()];
rep(i,1,n)if(cnt[i]>cnt[x])x=i;
rep(i,1,n)if(i!=x&&cnt[i]==cnt[x])printf("%d\n",n),exit(0);
rep(i,1,n)sum[i]=sum[i-1]+cnt[i];
per(i,n,1)b[sum[a[i]]--]=i;
rep(i,1,n)sum[i]+=cnt[i];
rep(i,1,n)if(a[i]==x)id[i]=++id[0],pos[id[0]]=i;
pos[id[0]+1]=n+1;
prework(1);
prework(-1);
sort(e+1,e+m+1,[&](sth x,sth y){return x.c<y.c||(x.c==y.c&&x.d<y.d);});
m=unique(e+1,e+m+1)-(e+1);
int p=1,q;
rep(i,1,n){
int *bb=b+sum[i-1],r=1,s;
for(;p<=m&&e[p].c==i;p=q+1,r=s+1){
for(q=p;q<m&&e[q+1].c==i&&e[q+1].d==e[q].d+1;q++);
int pp=e[p].d,qq=e[q].d;
for(s=r;s<cnt[i]&&bb[s+1]<pos[qq+1];s++);
solve(pos[pp-1],pos[qq+1],pos+pp-1,qq-pp+1,bb+r-1,s-r+1);
}
}
printf("%d\n",ans);
return 0;
}