【ZJOI2022】众数

一个显然的刻画是如果确定了选择的区间,那么答案就是区间内的众数出现次数加上区间外的众数出现次数。

对于与某个数出现次数有关的数的问题通常根号分治。称出现超过 \(\sqrt{n}\) 的数为“大数”,否则为“小数”。称区间内为“内层”,区间外为“外层”,不妨讨论内层众数和外层众数。

若内层众数或者外层众数有一个是大数。枚举该大数,先考虑该大数在外层。那么此时的复杂度允许我们枚举内层的众数,设枚举到的大数是 \(x\),出现次数为 \(w_1\),内层众数是 \(y\),出现次数为 \(w_2\)。将 \(x\) 出现的地方权值设为 \(-1\),将 \(y\) 出现的地方权值设为 \(-1\),那么求出最大子段和 \(s\),此时的答案就是 \(w_1+s\)

这个最大子段和可以直接 dp,单次和 \(y\) 相关,因此该部分的时间复杂度为 \(O(n\sqrt{n})\)

大数在内层的情况一样。考虑内外层均为小数的情况,看上去并不容易做,尝试将此时问题的约束变紧一点,若枚举外层小数 \(x\),则此时的区间数量为 \(O(w_x^2)\) 个(必然可以调整到 \(a_{l-1}=x\)\(a_{r+1}=x\))。简单分析一下得到上限 \(O(n\sqrt{n})\),因此我们只需要对这 \(O(n\sqrt{n})\) 个区间求众数及出现次数,不难规约到一下问题:

对于 \(O(n\sqrt{n})\) 个区间,均摊或不均摊地 \(O(1)\) 求出这些区间的众数和 \(\sqrt{n}\) 的较小值。

显然不均摊很难做。考虑均摊,利用双指针(或许是一堆指针)。扫描右端点,维护 \(p_{i,j}\) 代表对于此时右端点 \(i\),最大的一个左端点使得 \([p_{i,j},i]\) 的众数出现次数 \(\ge j\)

\(p\) 显然有单调性,同时 \(p\) 的第二维只有 \(O(\sqrt{n})\) 大小。先来考虑扫描 \(i\) 的时候如何维护 \(p_i\)。显然暴力移动 \(p_{i-1}\) 即可,有很多方法预处理,不作赘述。

此时对于一个要求众数的区间 \([l,r]\),在 \(p_l\) 上二分能得到答案,这里的复杂度多一个 \(\log\),不妨继续双指针,对于一个固定的 \(l\),利用双指针来求答案,复杂度为 \(O(n\sqrt{n})\)。这里要求 \(r\) 有序,可以简单做到。

总时间复杂度 \(O(n\sqrt{n})\)。当然上面写的主要是求出现次数,求到底有哪些数并不难维护。

#include<bits/stdc++.h>
// #define int long long
#define fi first
#define se second
#define pii std::pair<int,int>
#define vint std::vector<int>
#define vpair std::vector<pii>
#define rev(a) std::reverse(a.begin(),a.end())
#define debug(...) fprintf(stderr,##__VA_ARGS__)

template<typename T>
void read(T &x){
	x=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	x*=f;
}

std::stack<char>st;
template<typename T>
void print(T x){
	if(x==0) putchar('0');
	if(x<0) putchar('-'),x=-x;
	while(st.size()) st.pop();
	while(x) st.push((char)('0'+x%10)),x/=10;
	while(st.size()) putchar(st.top()),st.pop();
}

template<typename T>
void printsp(T x){
	print(x),putchar(' ');
}

template<typename T>
void println(T x){
	print(x),putchar('\n');
}

template<typename T,typename I>
bool chkmin(T &a,I b){
	if(a>b) return a=b,1;
	return 0;
}

template<typename T,typename I>
bool chkmax(T &a,I b){
	if(a<b) return a=b,1;
	return 0;
}

template<typename T,typename I>
void addedge(std::vector<I>*vec,T u,T v){
	vec[u].push_back(v);
}

template<typename T,typename I,typename K>
void addedge(std::vector<K>*vec,T u,T v,I w){
	vec[u].push_back({v,w});
}

template<typename T,typename I>
void addd(std::vector<I>*vec,T u,T v){
	addedge(vec,u,v),addedge(vec,v,u);
}

template<typename T,typename I,typename K>
void addd(std::vector<K>*vec,T u,T v,I w){
	addedge(vec,u,v,w),addedge(vec,v,u,w);
}

bool Mbe;

const int inf=1e9+1,MOD1=998244353,MOD2=1e9+7;

const int maxn=2e5+1,SQRT=210,B=200;

int a[maxn],T,n,b[maxn],f[maxn],ans,qzh[maxn],c[maxn],p[SQRT][maxn],t[maxn],s[maxn],zs[maxn],tot[maxn],bl[maxn],br[maxn],okk[maxn],ys[maxn];

vint vec,vecs,ql[maxn],qr[maxn],posvec;

vint h[maxn];

bool Men;

signed main(){
	// freopen("data.in","r",stdin),freopen("sol.out","w",stdout);
	debug("%.6lfMB\n",(&Mbe-&Men)/1048576.0);
	read(T);
	// debug("T=%d\n",T);
	while(T--){
		int tim=1;
		vec.clear(),vecs.clear(),posvec.clear(),ans=-inf;
		read(n);
		for(int i=1;i<=n;i++) read(a[i]),b[i]=a[i],h[i].clear(),qr[i].clear(),ql[i].clear(),tot[i]=0,okk[i]=0;
		for(int j=1;j<=B;j++)
			for(int i=1;i<=n;i++) p[j][i]=0;
		std::sort(b+1,b+n+1);
		for(int i=1;i<=n;i++) c[i]=std::lower_bound(b+1,b+n+1,a[i])-b,h[c[i]].push_back(i),ys[c[i]]=i;//h[c[i]] 存的是 c[i] 出现的位置
		int las=0;
		// debug("ok\n");
		for(int i=1;i<=n;i++)
			if(b[i]==b[las]) continue;
			else{
				if(i-las>=B&&las!=0) vec.push_back(b[las]),posvec.push_back(las);
				else vecs.push_back(las);
				las=i;
			}
		if(n+1-las>=B) vec.push_back(b[las]),posvec.push_back(las);
		else vecs.push_back(las);
		//存在大数
		for(int i=1;i<=n;i++) f[i]=0;
		int tnc=-1,tott=0,w1,w2,ff,ww,fff,ww2;
		for(int i:vec){
			tnc++;
			int c;
			//枚举此时外层的大数是 i
			w1=0,w2=0;//出现次数 || 最大子段和
			int sum=w1;//最后的答案就是 w1 + 最大子段和
			for(int j=1;j<=n;j++){
				qzh[j]=qzh[j-1];
				if(a[j]==i) w1++,qzh[j]++;
			}
			las=0;
			for(int j=1;j<=n;j++){
				if(b[j]==b[j-1]) continue;
				if(b[j]==i) continue;
				las=j;
				//枚举了内层的数是 b[j] -> 从 h[j] 中获得信息 -> 该数所有出现的位置为 h[j] 中的数
				w2=0;
				ff=0,las=0,ww=0,fff=0,ww2=0;
				for(int k:h[j]){
					tott++;
					//做最大子段和的 dp f[i]=max(f[i],0)+a[i] 放在这里的话我们在枚举关键点进行转移
					c=qzh[k]-qzh[las];
					ff=std::max(1,ff-c+1);
					chkmax(w2,ff);
					ww2++;
					chkmax(ww,fff+c);
					fff=fff+c-1;
					chkmax(fff,c-1);
					chkmax(ww,fff),chkmax(ww,c);
					las=k;
				}
				// debug("w1=%d\n",w1);
				if(chkmax(ans,w1+w2)) tim++;
				if(ans==w1+w2) okk[ys[posvec[tnc]]]=tim;
				c=qzh[n]-qzh[las];
				chkmax(ww,c);
				fff=fff+c;
				chkmax(ww,fff);
				if(chkmax(ans,ww2+ww)) tim++;
				if(ans==ww2+ww) okk[ys[j]]=tim;
			}
			if(chkmax(ans,w1)) tim++;
			if(w1==ans) okk[ys[posvec[tnc]]]=tim;
		}
		//全是小数的情况
		for(int j=1;j<=B;j++){
			for(int i=1;i<=n;i++) s[i]=0;
			for(int i=1;i<=n;i++){
				s[c[i]]++;
				if(s[c[i]]==j){
					p[j][1]=i;
					break;
				}
			}
			if(!p[j][1]){
				p[j][1]=n+1;
			}
			for(int i=2;i<=n;i++){
				//求出 p[i][j]
				s[c[i-1]]--;
				p[j][i]=p[j][i-1];
				if(s[c[i-1]]+1==j){
					bool fl=0;
					while(p[j][i]<n){
						p[j][i]++;
						s[c[p[j][i]]]++;
						if(s[c[p[j][i]]]==j){
							fl=1;
							break;
						}
					}
					if(!fl){
						p[j][i]=n+1;
					}
				}else p[j][i]=p[j][i-1];
			}
		}
		//能找出若干询问
		for(int i:vecs){
			// debug("i=%lld\n",i);	
			vint cl,cr;
			cl.push_back(1),cr.push_back(1);
			for(int j:h[i]){
				if(j<n-1) cl.push_back(j+1);
				if(j>2) cr.push_back(j-1);
			}
			cl.push_back(n),cr.push_back(n);
			rev(cr);
			for(int l:cl)
				for(int r:cr){
					if(l>r) break;
					//[l,r]
					qr[r].push_back(l);
				}
			tot[i]=h[i].size();
			int pos=1;
			for(int j:h[i]) bl[j]=pos,pos++;
			pos=1;
			rev(h[i]);
			for(int j:h[i]) br[j]=pos,pos++;
		}
		for(int i=1;i<=n;i++)
			for(int j:qr[i]) ql[j].push_back(i);
		//提前处理 i=1 避免空间问题
		int z=0;
		for(int i=0;i<B;i++){
			while(z<ql[1].size()&&ql[1][z]<p[i+1][1]){
				//[1,ql[1][z]]
				if(ql[1][z]==n) break;
				int sum=i;
				sum+=br[ql[1][z]+1];
				if(chkmax(ans,sum)) tim++;
				if(ans==sum) okk[ql[1][z]+1]=tim;
				z++;
			}
		}
		// debug("ok\n");
		for(int i=2;i<=n;i++){
			// debug("i=%lld\n",i);
			z=0;
			for(int j=0;j<B;j++)
				while(z<ql[i].size()&&ql[i][z]<p[j+1][i]){
					//[i,ql[i][z]] 答案为 j
					int sum=j+bl[i-1];
					if(ql[i][z]!=n) sum+=br[ql[i][z]+1];
					if(chkmax(ans,sum)) tim++;
					if(ans==sum) okk[i-1]=tim;
					z++;
				}
		}
		println(ans);
		vint sol;
		for(int i=1;i<=n;i++) if(okk[i]==tim) sol.push_back(a[i]);
		std::sort(sol.begin(),sol.end());
		auto zzz=std::unique(sol.begin(),sol.end());
		for(auto i=sol.begin();zzz!=i;i++) println(*i);
	}
	debug("%.6lfms\n",1e3*clock()/CLOCKS_PER_SEC);
}
/*
1
4
1 3 2 2

1
16
13 16 14 16 9 14 16 10 10 15 4 8 6 15 10 9 

1
7
5 7 3 5 4 4 4 

1
14
11 14 12 10 11 10 7 6 1 4 11 3 11 4 

1
18
6 11 2 2 11 6 14 2 3 5 3 8 13 2 11 12 10 10 
*/
posted @ 2025-02-04 18:45  BYR_KKK  阅读(53)  评论(0)    收藏  举报