【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;
}
posted @ 2021-12-11 11:56  alfalfa_w  阅读(103)  评论(0编辑  收藏  举报