題解[P5048 Yuno loves sqrt technology III]
阿新 • • 發佈:2021-08-12
題目連結
做此題前建議先做這題
雖然同樣是求區間眾數,但那題資料範圍小得多,空間限制也較鬆,這題還得另闢蹊徑。
考慮像那題一樣分塊,初始時用 \(O(n^{\frac{3}{2}})\) 的時間處理出兩兩塊中眾數的出現次數。
而由於空間限制不能處理出每個數在每個塊之前出現總次數,以處理散塊。
但事實上我們並不需要得知每個值真正的出現次數,只要看它能不能在區間中出現次數比當前答案大。
下以左散塊為例說明具體操作:
首先從左散塊的右端開始向 \(l\) 移動,設當前位置上的值為 \(x\) ,已得答案為 \(ans\) ,
那當前點的值能更新答案,就必須在區間內 \(x\) 之後 \(x\) 的個數比 \(ans\)
於是就可以把原序列離散化,然後對每個值存入這些值所在序列中位置,可以用指標實現,常數小。
同時記錄序列中每個位置 \(i\) 上的值 \(x\) 之前 \(x\) 出現過的次數 \(rev_i\) (包括這個位置)。
記 \(pos_{x,i}\) 為第 \(i\) 個值為 \(x\) 的數的位置,\(cnt_x\) 為值為 \(x\) 的數的總出現次數。
那麼當前值能更新答案的條件是 \(rev_i+ans\leq cnt_x\) 並且 \(pos_{x,rev_i+ans}\leq y\)
滿足條件時 \(ans\) 便可加一。
由於是從左散塊的右端開始向 \(l\) 移動,擴充套件範圍逐漸變大,能保證正確性(略有不同與其他題解,但常數更小)
而對於右散塊同樣從其左端點開始向 \(r\)
\(rev_i > ans\) 並且 \(pos_{x,rev_i-ans}\geq l\)
同樣由於計算區間是逐漸擴大,正確性依然能保證。
時間複雜度: \(O(n^{\frac{3}{2}})\) 空間複雜度:\(O(n)\)
程式碼:(這樣做常數十分小,一點也不卡常)
#include<bits/stdc++.h> using namespace std; const int N=5e5+10,NN=710; int n,m,x,y,nn,_n,sn,ans,last;char ch; int a[N],b[N],id[N],l[N],r[N],res[NN][NN],cnt[N],tmp[N],rev[N],*pos[N]; inline void read(int &x){ x=0;ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); } inline void write(int x){if(x>=10)write(x/10);putchar('0'+x%10);} inline void inquiry(int x,int y){ ans=res[id[x]+1][id[y]-1];register int i; if(id[x]==id[y]){ for(i=x;i<=y;++i)ans=max(ans,++tmp[a[i]]); for(i=x;i<=y;++i)--tmp[a[i]]; } else { for(i=r[id[x]];i>=x;--i)if(rev[i]+ans<=cnt[a[i]])if(pos[a[i]][rev[i]+ans]<=y)++ans; for(i=l[id[y]];i<=y;++i)if(rev[i]>ans)if(pos[a[i]][rev[i]-ans]>=x)++ans; } } main(){ read(n),read(m);nn=sqrt(n);sn=(n-1)/nn+1;register int i,j,k; for(i=1;i<=n;++i)read(a[i]),b[i]=a[i],id[i]=(i-1)/nn+1; for(i=1;i<=n;++i)r[id[i]]=i,l[id[n-i+1]]=n-i+1; sort(b+1,b+n+1);_n=unique(b+1,b+n+1)-b-1; for(i=1;i<=n;++i)a[i]=lower_bound(b+1,b+_n+1,a[i])-b,++cnt[a[i]]; for(i=1;i<=_n;++i)pos[i]=new int [cnt[i]+1]; for(i=1;i<=n;++i)pos[a[i]][++tmp[a[i]]]=i,rev[i]=tmp[a[i]]; for(i=1;i<=sn;++i){ for(j=1;j<=_n;++j)tmp[j]=0; for(j=i;j<=sn;++j){ res[i][j]=res[i][j-1]; for(k=l[j];k<=r[j];++k)res[i][j]=max(res[i][j],++tmp[a[k]]); } } for(j=1;j<=_n;++j)tmp[j]=0; while(m--){ read(x),read(y);x^=last,y^=last; inquiry(x,y);write(last=ans);putchar('\n'); } }