bzoj 2724: [Violet 6]蒲公英 區間眾數 分塊
阿新 • • 發佈:2019-02-11
Description
求區間眾數,強制線上
縮減了一下題意,比較清晰明瞭。
對於該問題我們可以首先考慮分塊,對於一個連續的區間,肯定由根號n個小塊以及根號n個大塊組成,那麼答案可能是哪些呢,一定是大塊從左到右的眾數a,或者是某一個小塊的數。那大塊裡的其他數呢,可以考慮一下,既然大塊裡眾數已經是之前的a了,那麼其他的數想成為眾數,只能是加上外面小塊的數,而外面的則會被直接計算。
接著是如何統計一個區間內一個數字的個數,最簡單的方法是大塊內排序用lower_bound logn查詢,可是這樣複雜度變成了n * 根號n * 根號n * logn,連暴力都不如了。仔細想想,我們可以利用一下數字位置的特性,對於每一個數字開一個vector來儲存這個數字出現的每一個位置,這樣對於每個數字lower_bound查一下出現的區間即可,複雜度就成功變成了n *
根號n * logn。
最後一個問題就是如何求大塊到大塊之間的眾數了,我們以每一個大塊的左端為開頭,向右一塊一塊暴力加,開一個cnt陣列輔助記錄出現次數即可。
下附AC程式碼
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<vector> #include<math.h> #include<map> #define maxn 10005 using namespace std; int n,q,m; int tot; int a[maxn]; int cnt[maxn]; int dat[maxn]; int bel[maxn]; int rea[maxn]; int dp[505][505]; map<int,int> num; int l[maxn],r[maxn]; vector<int>edge[maxn]; void init(int now) { memset(cnt,0,sizeof(cnt)); int ans=0,maxx=0; for(int i=l[now];i<=n;i++) { cnt[a[i]]++; if(cnt[a[i]]>=ans || (cnt[a[i]]==ans && rea[a[i]]<rea[maxx])) { ans=cnt[a[i]]; maxx=a[i]; } dp[now][bel[i]]=maxx; } } int getsum(int nl,int nr,int v) { int ans1=*lower_bound(edge[v].begin(),edge[v].end(),nl); int ans2=*lower_bound(edge[v].begin(),edge[v].end(),nr); if(ans2==*edge[v].end() || edge[v][ans2]>nr) ans2--; return ans2-ans1+1; } int query(int nl,int nr) { int ans=0,maxx=0; if(bel[nr]-bel[nl]<=2) { for(int i=nl;i<=nr;i++) { int now=getsum(nl,nr,a[i]); if(now>ans || (now==ans && rea[a[i]]<rea[maxx])) { ans=now; maxx=a[i]; } } return maxx; } else { for(int i=nl;i<=r[bel[nl]];i++) { int now=getsum(nl,nr,a[i]); if(now>ans || (now==ans && rea[a[i]]<rea[maxx])) { ans=now; maxx=a[i]; } } for(int i=l[bel[nr]];i<=nr;i++) { int now=getsum(nl,nr,a[i]); if(now>ans || (now==ans && rea[a[i]]<rea[maxx])) { ans=now; maxx=a[i]; } } int temp=dp[bel[nl]+1][bel[nr]-1]; int now=getsum(nl,nr,temp); if(now>ans || (now==ans && rea[temp]<rea[maxx])) { ans=now; maxx=temp; } return maxx; } } int main() { memset(l,0x3f,sizeof(l)); scanf("%d%d",&n,&q); m=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(!num[a[i]]) { num[a[i]]=++tot; rea[tot]=a[i]; } a[i]=num[a[i]]; edge[a[i]].push_back(i); bel[i]=((i-1)/m)+1; l[((i-1)/m+1)]=min(l[((i-1)/m+1)],i); r[((i-1)/m+1)]=max(r[((i-1)/m+1)],i); } for(int i=1;i<=((n-1)/m)+1;i++) { init(i); } int pre=0; while(q--) { int nl,nr; scanf("%d%d",&nl,&nr); nl=(nl+pre-1)%n+1; nr=(nr+pre-1)%n+1; if(nl>nr) swap(nl,nr); pre=query(nl,nr); printf("%d\n",rea[pre]); } }