ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P8330-[ZJOI2022]众数【根号分治】

2022-06-08 19:34:14  阅读:169  来源: 互联网

标签:P8330 次数 int sqrt leq 众数 ZJOI2022 include 根号


正题

题目链接:https://www.luogu.com.cn/problem/P8330


题目大意

给出一个长度为\(n\)的序列\(a\),你可以选择其中一个区间将其加上任意整数,要求这个序列的众数出现次数最多。

输出最多次数和可能的众数。

\(1\leq n\leq 2\times 10^5,1\leq a_i\leq 10^9,\sum n\leq 5\times 10^5\),保证不所有数都相等。


解题思路

相当于找到一个区间使得区间外和区间内的众数次数和最大。

这个和出现次数挂钩,考虑根号分治。对于出现次数大于\(\sqrt n\)的数字,这种数字不会超过\(\sqrt n\)个,可以考虑对每个数字暴力做。

假设在区间外的数字是\(x\),区间内的是\(y\),那么我们区间中每个\(x\)会令答案\(-1\),每个\(y\)会令答案\(+1\)。将\(x\)的位置视为\(-1\),\(y\)的位置视为\(1\),那么最大答案就是\(x\)的出现次数加最大子段和。

这个复杂度可以做到\(min(c_x,c_y)\),其中\(c_x\)表示\(x\)的出现次数。

那对于每个\(c_x>\sqrt n\)的我们都可以\(O(n)\)解决它在外或者在内的情况。

然后考虑\(c_x\leq \sqrt n\)且\(c_y\leq \sqrt n\)的情况,先把所有\(c_x>\sqrt n\)的\(x\)给去掉,此时注意到任何区间的众数个数都是\(\leq \sqrt n\)的,那么我们预处理出\(f_{l,i}\)表示一个最小的\(r\),满足\([l,r]\)的众数出现次数为\(i\),这样我们对于每个在外面的\(x\)枚举两个\(x\)的位置作为端点,然后单指针移动计算他们之间区间的众数出现次数即可。

时间复杂度:\(O(n\sqrt n)\)


解题思路

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<vector>
#include<cmath>
using namespace std;
const int N=2e5+10,M=450;
int n,T,a[N],b[N],r[N][M],s[N];
vector<int> v[N],pr[N];
int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
void calc(int x,int y){
	int ans1=0,ans2=0,now1=0,now2=0,las=0;
	for(int i=0;i<v[y].size();i++){
		now1=max(now1+s[v[y][i]]-s[las],0);
		ans1=max(ans1,now1);now1=max(now1-1,0);
		now2=max(now2-s[v[y][i]]+s[las],1);
		ans2=max(ans2,now2);
		las=v[y][i];
	}
	pr[ans1+v[y].size()].push_back(y);
	pr[ans2+v[x].size()].push_back(x);
	return;
}
int main()
{
//	freopen("mode_ex2.in","r",stdin);
	int cas=read();
	while(cas--){
		n=read();T=445;
		for(int i=1;i<=n;i++)pr[i].clear(),v[i].clear();
		for(int i=1;i<=n+1;i++)
			for(int j=0;j<T;j++)r[i][j]=n+1;
		for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i];
		sort(b+1,b+1+n);
		int m=unique(b+1,b+1+n)-b-1;
		for(int i=1;i<=n;i++){
			a[i]=lower_bound(b+1,b+1+m,a[i])-b;
			v[a[i]].push_back(i);
		}
		for(int x=1;x<=m;x++){
			pr[v[x].size()].push_back(x);
			if(v[x].size()>T){
				for(int i=1;i<=n;i++)s[i]=0;
				for(int i=0;i<v[x].size();i++)s[v[x][i]]++;
				for(int i=1;i<=n;i++)s[i]+=s[i-1];
				for(int i=1;i<=m;i++)
					if(i!=x)calc(x,i);
			}
		}
		for(int x=1;x<=m;x++){
			if(v[x].size()<=T){
				for(int i=0;i<v[x].size();i++)
					for(int j=i;j<v[x].size();j++)
						r[v[x][i]][j-i]=v[x][j];
			}
		}
		for(int i=n;i>=1;i--)
			for(int j=0;j<T;j++)
				r[i][j]=min(r[i][j],r[i+1][j]);
		for(int x=1;x<=m;x++){
			if(v[x].size()<=T){
				for(int i=-1;i<(int)v[x].size();i++){
					int l=(i==-1)?0:v[x][i];l++;
					for(int j=0,z=i+1;j<T;j++){
						if(r[l][j]>n)break;
						while(z<v[x].size()&&v[x][z]<=r[l][j])z++;
						pr[v[x].size()+j+1-(z-i-1)].push_back(x);
					}
				}
			}
		}
		int ans=0;
		for(int i=n;i>=1;i--)
			if(pr[i].size()){ans=i;break;}
		printf("%d\n",ans);
		sort(pr[ans].begin(),pr[ans].end());
		printf("%d\n",b[pr[ans][0]]);
		for(int i=1;i<pr[ans].size();i++)
			if(pr[ans][i-1]!=pr[ans][i])
				printf("%d\n",b[pr[ans][i]]);
	}
	return 0;
}

标签:P8330,次数,int,sqrt,leq,众数,ZJOI2022,include,根号
来源: https://www.cnblogs.com/QuantAsk/p/16356853.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有