洛谷 - P4168 [Violet]蒲公英(分块+离散化)
題目鏈接:點擊查看
題目大意:給出一個長度為 n 的數列,再給出 m 次查詢,每次查詢區間 [ l , r ] 內的眾數,要求強制在線
題目分析:對于這個題意來說,如果允許離線的話,完全可以用莫隊當模板題水過去,但強制在線的話就需要考慮如何分塊了
這里只講一下如何實現大藍書上的第二個方法吧
首先設將數列 n 分成 T 塊,每塊的長度為 n / T ,接下來可以預處理出 mode[ l ][ r ] ,代表包含第 l 塊到第 r 塊在內的這段區間上的眾數是多少,時間復雜度為 O( T * T * n/T ) = O( nT ),空間復雜度為 O( T^2 )
然后再預處理出一個 vector 的數組 pos,其中 pos[ val ] 中包含數字 val 出現的所有位置,這樣就能二分查找出 val 在區間 [ l , r ] 上的出現次數了,預處理的時間復雜度為 O( n ) ,空間復雜度為 O( n )
最后對于每次查詢,設 ll 為 l 所在的分塊,rr 為 r 所在的分塊,可以 O( 1 ) 查詢 [ ll + 1 , rr - 1 ] 內的眾數即 mode[ ll + 1 ][ rr - 1 ] ,對于兩端多余的部分,可以暴力遍歷,每次二分查找并維護最大值,時間復雜度為 O( m * n/T?* logn )
根據均值不等式:?nT = m * n/T * logn,解得 T = sqrt( m * logn )
然后分塊亂搞就好啦,注意每個數的范圍比較大,需要離散化一下
在查詢時有個小技巧,因為 mode[ l ][ r ] 的 l?和 r 需要滿足 l <= r ,而當 l = r - 1 時會出現 l + 1 < r - 1 的情況,對于這種情況,我們不妨一起暴力處理,只不過時間復雜度多加了一個常數,但是實現起來卻簡單了不少
代碼:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=4e4+100;int n,m,t,L[N],R[N],a[N],belong[N],mode[1010][1010],cnt[N];vector<int>disc,pos[N];void build()//分塊 {t=max(1,(int)sqrt(m*log2(n)));//多少塊int block=n/t;//每塊的長度 for(int i=1;i<=t;i++){L[i]=block*(i-1)+1;R[i]=block*i;}if(R[t]<n){t++;L[t]=R[t-1]+1;R[t]=n;}for(int i=1;i<=t;i++)for(int j=L[i];j<=R[i];j++)belong[j]=i; }void discreate()//離散化 {sort(disc.begin(),disc.end());disc.erase(unique(disc.begin(),disc.end()),disc.end());for(int i=1;i<=n;i++)a[i]=lower_bound(disc.begin(),disc.end(),a[i])-disc.begin(); }void init() {for(int i=1;i<=n;i++)pos[a[i]].push_back(i);for(int i=1;i<=t;i++){memset(cnt,0,sizeof(cnt));int mmax=-1,mark=0;for(int j=i;j<=t;j++){for(int k=L[j];k<=R[j];k++){cnt[a[k]]++;if(cnt[a[k]]>mmax||cnt[a[k]]==mmax&&a[k]<mark){mmax=cnt[a[k]];mark=a[k];}}mode[i][j]=mark;}} }int get_num(int l,int r,int val) {return upper_bound(pos[val].begin(),pos[val].end(),r)-lower_bound(pos[val].begin(),pos[val].end(),l); }int query(int l,int r) {int ll=belong[l],rr=belong[r],ans;if(rr-ll<=1){int mmax=-1;for(int i=l;i<=r;i++){int cnt=get_num(l,r,a[i]);if(cnt>mmax||cnt==mmax&&a[i]<ans){mmax=cnt;ans=a[i];}}}else{ans=mode[ll+1][rr-1];int mmax=get_num(l,r,ans);for(int i=l;i<=R[ll];i++){int cnt=get_num(l,r,a[i]);if(cnt>mmax||cnt==mmax&&a[i]<ans){mmax=cnt;ans=a[i];}}for(int i=L[rr];i<=r;i++){int cnt=get_num(l,r,a[i]);if(cnt>mmax||cnt==mmax&&a[i]<ans){mmax=cnt;ans=a[i];}}}return ans; }int main() { #ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); #endif // ios::sync_with_stdio(false);scanf("%d%d",&n,&m);build();for(int i=1;i<=n;i++){scanf("%d",a+i);disc.push_back(a[i]);}discreate();init();int pre=0;while(m--){int l,r;scanf("%d%d",&l,&r);l=(l+pre-1)%n+1;r=(r+pre-1)%n+1;if(l>r)swap(l,r);printf("%d\n",pre=disc[query(l,r)]);}return 0; }?
總結
以上是生活随笔為你收集整理的洛谷 - P4168 [Violet]蒲公英(分块+离散化)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ - 3468 A Simple
- 下一篇: 牛客 - 数位操作2(数位dp)