P5048-[Ynoi2019 模擬賽]Yuno loves sqrt technology III【分塊】
阿新 • • 發佈:2021-07-13
正題
題目連結:https://www.luogu.com.cn/problem/P5048
題目大意
就是這個
【QA】區間眾數,但空間很小
長度為\(n\)的序列,要求支援查詢區間眾數出現次數。
強制線上
\(1\leq n,m\leq 5\times 10^5\)
解題思路
空間小就不能用蒲公英那種做法了
分塊然後處理出每個連續塊段的眾數,就是設\(f_{l,r}\)表示從塊\(l\sim r\)的區間眾數出現次數。
然後考慮散塊的部分,如果散塊會更新答案那麼顯然新的眾數一定是出現在散塊裡的,所以答案增加不會超過\(2\sqrt n\)
用\(vector\)記錄每個數字出現的位置,然後對於散塊的每個數字我們看一下\(ans\)
時間複雜度\(O(n\sqrt n)\)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cmath> using namespace std; const int N=5e5+10,M=710; int n,m,cnt,pos[N],a[N],b[N],c[N],w[N],L[M],R[M],f[M][M]; vector<int>v[N]; int Ask(int l,int r){ int q=pos[l],p=pos[r]; if(q==p){ int ans=0; for(int i=l;i<=r;i++) ++c[a[i]],ans=max(ans,c[a[i]]); for(int i=l;i<=r;i++)c[a[i]]=0; return ans; } int ans=f[q+1][p-1]; for(int i=l;i<=R[q];i++) while(w[i]+ans<v[a[i]].size()&&v[a[i]][w[i]+ans]<=r)ans++; for(int i=L[p];i<=r;i++) while(w[i]-ans>=0&&v[a[i]][w[i]-ans]>=l)ans++; return ans; } int main() { scanf("%d%d",&n,&m); int T=sqrt(n); for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+1+n); int mnt=unique(b+1,b+1+n)-b-1; for(int i=1;i<=n;i++){ a[i]=lower_bound(b+1,b+1+mnt,a[i])-b; v[a[i]].push_back(i); w[i]=v[a[i]].size()-1; } for(int i=1;i*T<=n;i++) ++cnt,L[cnt]=R[cnt-1]+1,R[cnt]=i*T; if(R[cnt]<n)++cnt,L[cnt]=R[cnt-1]+1,R[cnt]=n; for(int i=1;i<=cnt;i++) for(int j=L[i];j<=R[i];j++)pos[j]=i; for(int i=1;i<=cnt;i++){ for(int j=i;j<=cnt;j++){ f[i][j]=f[i][j-1]; for(int k=L[j];k<=R[j];k++) ++c[a[k]],f[i][j]=max(f[i][j],c[a[k]]); } for(int k=L[i];k<=n;k++)c[a[k]]=0; } int last=0; while(m--){ int l,r; scanf("%d%d",&l,&r); l^=last;r^=last; printf("%d\n",last=Ask(l,r)); } return 0; }