ZJOI 2022 題目選做
阿新 • • 發佈:2022-05-16
眾數
題目描述
解法
眾數問題基本上不能 \(poly\ log\),所以我們著重考慮一些根號演算法。
可以對不同顏色的出現次數根號分治,稱出現次數 \(\geq \sqrt n\) 的為大顏色,否則為小顏色。
首先考慮大顏色對其他顏色的貢獻,可以把大顏色打在原來的序列上,做完字首和之後列舉其他顏色,按順序掃描,記錄字首最小值就可以做到 \(O(n)\),這一部分總時間 \(O(n\sqrt n)\)
然後考慮小顏色對其他顏色的貢獻,這時候要抓住關鍵性質:選取的區間眾數應當 \(\leq \sqrt n\),那麼我們外層列舉眾數大小 \(x\),然後雙指標就可以獲得 \(p_r\) 表示右端點 \(r\)
#include <cstdio> #include <vector> #include <iostream> #include <algorithm> #include <cmath> using namespace std; const int M = 200005; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int T,n,m,k,a[M],b[M],c[M],s[M],ans[M],p[M],mi[M]; vector<int> g[M]; void work() { n=read();k=sqrt(n); for(int i=1;i<=n;i++) a[i]=b[i]=read(),c[i]=ans[i]=0,g[i].clear(); sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1; for(int i=1;i<=m;i++) g[i].push_back(0); for(int i=1;i<=n;i++) { c[a[i]=lower_bound(b+1,b+1+m,a[i])-b]++; g[a[i]].push_back(i); } for(int i=1;i<=m;i++) g[i].push_back(n+1); for(int x=1;x<=m;x++) if(c[x]>=k) { for(int i=1;i<=n;i++) s[i]=s[i-1]+(a[i]==x); s[n+1]=s[n]; for(int y=1;y<=m;y++) if(x^y) { int mn=0; for(int i=1;i<g[y].size();i++) { int nw=s[g[y][i]]-i; ans[y]=max(ans[y],nw-mn+1); mn=min(mn,nw); } } } for(int x=k;x>=1;x--) { for(int i=1;i<=m;i++) s[i]=0; for(int r=1,l=1;r<=n;r++) { s[a[r]]++; while(s[a[r]]>=x) s[a[l]]--,l++; p[r]=l; } for(int y=1;y<=m;y++) if(ans[y]<x) { vector<int> &t=g[y]; for(int i=1,j=0,k=0;i<t.size();i++) { if(p[k=t[i]-1]<2) continue; while(j<i && t[j+1]<p[k]-1) j++; if(j<i) ans[y]=max(ans[y],x-(i-j-1)); } } } int mx=0; for(int i=1;i<=m;i++) mx=max(mx,ans[i]+c[i]); printf("%d\n",mx); for(int i=1;i<=m;i++) if(ans[i]+c[i]==mx) printf("%d\n",b[i]); } signed main() { T=read(); while(T--) work(); }