正题
题目链接:https://www.luogu.com.cn/problem/P8330
题目大意
给出一个长度为nnn的序列aaa,你可以选择其中一个区间将其加上任意整数,要求这个序列的众数出现次数最多。
输出最多次数和可能的众数。
1≤n≤2×105,1≤ai≤109,∑n≤5×1051\leq n\leq 2\times 10^5,1\leq a_i\leq 10^9,\sum n\leq 5\times 10^51≤n≤2×105,1≤ai≤109,∑n≤5×105,保证不所有数都相等。
解题思路
相当于找到一个区间使得区间外和区间内的众数次数和最大。
这个和出现次数挂钩,考虑根号分治。对于出现次数大于n\sqrt nn的数字,这种数字不会超过n\sqrt nn个,可以考虑对每个数字暴力做。
假设在区间外的数字是xxx,区间内的是yyy,那么我们区间中每个xxx会令答案−1-1−1,每个yyy会令答案+1+1+1。将xxx的位置视为−1-1−1,yyy的位置视为111,那么最大答案就是xxx的出现次数加最大子段和。
这个复杂度可以做到min(cx,cy)min(c_x,c_y)min(cx,cy),其中cxc_xcx表示xxx的出现次数。
那对于每个cx>nc_x>\sqrt ncx>n的我们都可以O(n)O(n)O(n)解决它在外或者在内的情况。
然后考虑cx≤nc_x\leq \sqrt ncx≤n且cy≤nc_y\leq \sqrt ncy≤n的情况,先把所有cx>nc_x>\sqrt ncx>n的xxx给去掉,此时注意到任何区间的众数个数都是≤n\leq \sqrt n≤n的,那么我们预处理出fl,if_{l,i}fl,i表示一个最小的rrr,满足[l,r][l,r][l,r]的众数出现次数为iii,这样我们对于每个在外面的xxx枚举两个xxx的位置作为端点,然后单指针移动计算他们之间区间的众数出现次数即可。
时间复杂度:O(nn)O(n\sqrt n)O(nn)
解题思路
#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;
}