【ZJOI2022】众数
一个显然的刻画是如果确定了选择的区间,那么答案就是区间内的众数出现次数加上区间外的众数出现次数。
对于与某个数出现次数有关的数的问题通常根号分治。称出现超过
若内层众数或者外层众数有一个是大数。枚举该大数,先考虑该大数在外层。那么此时的复杂度允许我们枚举内层的众数,设枚举到的大数是
这个最大子段和可以直接 dp,单次和
大数在内层的情况一样。考虑内外层均为小数的情况,看上去并不容易做,尝试将此时问题的约束变紧一点,若枚举外层小数
对于
个区间,均摊或不均摊地 求出这些区间的众数和 的较小值。
显然不均摊很难做。考虑均摊,利用双指针(或许是一堆指针)。扫描右端点,维护
此时对于一个要求众数的区间
总时间复杂度
#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 */
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】