(就是亂搞)c++
咱們考慮一個問題:區間修改單點查詢,n,m<=1e5;
算法
那麼咱們能夠怎麼解決這個問題呢?數組
線段樹!樹狀數組!數據結構
分塊~!ide
分塊是何物呢:是一種基於暴力的算法(優雅的暴力嗷)spa
咱們考慮以下一種玄學方法:3d
將整個序列分爲幾大塊,維護每一個大塊的和,單點修改顯然能夠O(1)實現;code
那麼區間查詢怎麼辦呢?假設查詢區間是[L,R],那麼能夠確定的是,若是[L,R]內包含大塊,咱們只要加上大塊的和就能夠惹,至於不在大塊內的數,咱們能夠暴力求和嘛qwq;blog
雖然這樣聽起來也會令你T到昇天,可是老是比n^2快了一些對吧;排序
那麼這麼作的時間複雜度究竟是多少呢?咱們來分析一下;
首先,假設咱們把整個序列長度爲N,分紅了M塊,那麼每次查詢的複雜度爲:O(M+N/M);
這個複雜度到底在什麼範圍內呢?
咱們根據均值(基本)不等式能夠得出(M+N/M)<=2√(N);在M==N/M時取到最小值;
也就是嗦,當M=√N時,總複雜度最小,爲√N;
那麼解決問題總複雜就是O(N+M√N)的,怎麼樣,是否是還能夠接受;
咱們來看另外一個問題:
區間加,區間小於k的個數,n,m<=1e5;
線段樹玩家已暴斃
咱們仍然能夠用分塊來求解:
先將每一個塊進行塊內排序,複雜度nlog√n;
區間小於k個個數求法就很顯然了:對於不在整塊內的部分,暴力統計;整塊內的部分二分查找,每次處理複雜度√Nlog√N;
區間加法怎麼辦呢?
對於整塊,區間加法顯然不破壞大小關係,直接打上一個標記便可;
對於散塊,因爲散塊最多有2塊,顯然咱們能夠直接打破,暴力重構,從新排序,複雜度√Nlog√N;
總複雜度O(Nlog√N+M√Nlog√N);
發現了嗎,因爲分塊算法犧牲了部分時間複雜度,因此能夠處理的信息更加靈活,相比與傳統區間數據結構,分塊算法能夠處理
這裏借用黃學長的分塊入門系列qwq;
分塊入門1:
區間加法,單點查詢;
上面講過惹qwq;
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,cnt; int a[100010]; int bel[100010],tag[100010]; inline void update(int l,int r,int k) { int lx=bel[l],rx=bel[r]; for(int i=l;bel[i]==lx&&i<=r;++i) a[i]+=k; if(lx==rx) return ; for(int i=r;bel[i]==rx;--i) a[i]+=k; for(int i=lx+1;i<rx;++i) tag[i]+=k; } signed main() { n=read(); cnt=sqrt(n); for(int i=1;i<=n;++i) { a[i]=read(); bel[i]=(i-1)/cnt+1; } for(int t,x,y,z,i=1;i<=n;++i) { t=read(),x=read(),y=read(),z=read(); if(!t) update(x,y,z); else printf("%lld\n",a[y]+tag[bel[y]]); } return 0; }
分塊入門2:
區間加法,區間小於k個數;
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,cnt,sum; int a[100010],bel[100010],tag[100010]; int c[500][500]; inline void sor(int x) { memset(c[x],0,sizeof(c[x])); for(int i=(x-1)*cnt+1;i<=min(n,x*cnt);++i) { c[x][++c[x][0]]=a[i]; } sort(c[x]+1,c[x]+c[x][0]+1); } inline void update(int x,int y,int k) { int lx=bel[x],rx=bel[y]; for(int i=x;bel[i]==lx&&i<=y;++i) a[i]+=k; sor(lx); if(lx==rx) return ; for(int i=y;bel[i]==rx;--i) a[i]+=k; sor(rx); for(int i=lx+1;i<rx;++i) tag[i]+=k; } inline int query(int x,int y,int k) { int res=0; int lx=bel[x],rx=bel[y]; for(int i=x;bel[i]==lx&&i<=y;++i) { if(a[i]+tag[lx]<k) ++res; } if(lx==rx) return res; for(int i=y;bel[i]==rx;--i) { if(a[i]+tag[rx]<k) ++res; } for(int i=lx+1;i<rx;++i) { int l=1,r=c[i][0],ans=0; while(l<=r) { int mid=(l+r)>>1; if(c[i][mid]+tag[i]>=k) r=mid-1; else ans=mid,l=mid+1; } res+=ans; } return res; } signed main() { n=read(); cnt=sqrt(n); sum=n/cnt+(n%cnt>0); for(int i=1;i<=n;++i) { a[i]=read(); bel[i]=(i-1)/cnt+1; c[bel[i]][++c[bel[i]][0]]=a[i]; } for(int i=1;i<=sum;++i) { sort(c[i]+1,c[i]+c[i][0]+1); } for(int t,x,y,z,i=1;i<=n;++i) { t=read(),x=read(),y=read(),z=read(); if(!t) update(x,y,z); else printf("%lld\n",query(x,y,z*z)); } return 0; }
分塊入門3:
區間加法,區間小於k前驅;
顯然能夠沿用上一題的方法,塊內排序+二分;
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,cnt,sum; int a[100010],bel[100010],tag[100010]; int c[500][500]; inline void sor(int x) { memset(c[x],0,sizeof(c[x])); for(int i=(x-1)*cnt+1;i<=min(n,x*cnt);++i) c[x][++c[x][0]]=a[i]; sort(c[x]+1,c[x]+c[x][0]+1); } inline void update(int x,int y,int k) { int lx=bel[x],rx=bel[y]; for(int i=x;i<=y&&bel[i]==lx;++i) a[i]+=k; sor(lx); if(lx==rx) return ; for(int i=y;bel[i]==rx;--i) a[i]+=k; sor(rx); for(int i=lx+1;i<rx;++i) tag[i]+=k; } inline int query(int x,int y,int k) { int lx=bel[x],rx=bel[y]; int ans=-1; for(int i=x;i<=y&&bel[i]==lx;++i) { if(a[i]+tag[lx]<k) ans=max(ans,a[i]+tag[lx]); } if(lx==rx) return ans; for(int i=y;bel[i]==rx;--i) { if(a[i]+tag[rx]<k) ans=max(ans,a[i]+tag[rx]); } for(int i=lx+1;i<rx;++i) { int l=1,r=c[i][0]; while(l<=r) { int mid=(l+r)>>1; if(c[i][mid]+tag[i]<k) ans=max(ans,c[i][mid]+tag[i]),l=mid+1; else r=mid-1; } } return ans; } signed main() { n=read(); cnt=sqrt(n); sum=n/cnt+(n%cnt>0); for(int i=1;i<=n;++i) { a[i]=read(); bel[i]=(i-1)/cnt+1; c[bel[i]][++c[bel[i]][0]]=a[i]; } for(int i=1;i<=sum;++i) sort(c[i]+1,c[i]+c[i][0]+1); for(int t,x,y,z,i=1;i<=n;++i) { t=read(),x=read(),y=read(),z=read(); if(!t) update(x,y,z); else printf("%lld\n",query(x,y,z)); } return 0; }
分塊入門4:
區間加法,區間求和;
區間求和已經講過了,至於區間加法,在整塊上打上標記表示這一塊加上了多少,散塊直接暴力添加,修改塊和;
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,cnt; int a[100010],bel[100010],tag[100010]; int sum[100010]; inline void update(int x,int y,int k) { int lx=bel[x],rx=bel[y]; for(int i=x;i<=y&&bel[i]==lx;++i) a[i]+=k,sum[lx]+=k; if(lx==rx) return ; for(int i=y;bel[i]==rx;--i) a[i]+=k,sum[rx]+=k; for(int i=lx+1;i<rx;++i) tag[i]+=k; } inline int query(int x,int y,int lyy) { int lx=bel[x],rx=bel[y],res=0; for(int i=x;i<=y&&bel[i]==lx;++i) res = ( res + a[i] + tag[lx] ) % lyy ; if(lx==rx) return res%lyy; for(int i=y;bel[i]==rx;--i) res = ( res + a[i] + tag[rx] ) % lyy ; for(int i=lx+1;i<rx;++i) res = ( res + sum[i] + ( cnt * tag[i] ) % lyy ) % lyy ; return res%lyy; } signed main() { n=read(); cnt=sqrt(n); for(int i=1;i<=n;++i) { a[i]=read(); bel[i]=(i-1)/cnt+1; sum[bel[i]]+=a[i]; } for(int t,x,y,z,i=1;i<=n;++i) { t=read(),x=read(),y=read(),z=read(); if(!t) update(x,y,z); else printf("%lld\n",query(x,y,z+1)); } return 0; }
分塊入門5:
區間求和,區間開方(下取整);
啥啥啥這是啥???區間開方?這是個啥?(我剛看到這個題目心裏OS)
不過仔細思考,開方還要下取整,那豈不是1和0怎麼開不變咯;
分析每一個數據小於2^31-1,那麼一個數最多被開方5次,因此咱們每次只要在對一個區間進行暴力開方後,判斷這個區間是否所有變成了0or1,若是是,打上一個標記下次跳過;
因爲每一個數只能被開方5次,那麼總複雜度O(5*N+M√N);
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,cnt; int a[100010],bel[100010],tag[100010]; int sum[100010]; inline void sqr(int x) { bool flag=0; for(int i=(x-1)*cnt+1;i<=min(n,cnt*x);++i) { sum[x]-=a[i]; sum[x]+=(int)sqrt(a[i]); a[i]=sqrt(a[i]); if(a[i]>1) flag=1; } if(!flag) tag[x]=1; } inline void update(int x,int y) { int lx=bel[x],rx=bel[y]; for(int i=x;i<=y&&bel[i]==lx;++i) { sum[lx]-=a[i]; sum[lx]+=(int)sqrt(a[i]); a[i]=sqrt(a[i]); } if(lx==rx) return ; for(int i=y;bel[i]==rx;--i) { sum[rx]-=a[i]; sum[rx]+=(int)sqrt(a[i]); a[i]=sqrt(a[i]); } for(int i=lx+1;i<rx;++i) { if(tag[i]) continue; else sqr(i); } } inline int query(int x,int y) { int lx=bel[x],rx=bel[y]; int res=0; for(int i=x;i<=y&&bel[i]==lx;++i) res+=a[i]; if(lx==rx) return res; for(int i=y;bel[i]==rx;--i) res+=a[i]; for(int i=lx+1;i<rx;++i) res+=sum[i]; return res; } signed main() { n=read(); cnt=sqrt(n); for(int i=1;i<=n;++i) { a[i]=read(); bel[i]=(i-1)/cnt+1; sum[bel[i]]+=a[i]; } for(int t,x,y,z,i=1;i<=n;++i) { t=read(),x=read(),y=read(),z=read(); if(!t) update(x,y); else printf("%lld\n",query(x,y)); } return 0; }
分塊入門6:
單點插入單點查詢,數據隨機;
考慮到數據隨機,統計塊內數字數量,用鏈表插入便可;
可是有一個問題:數據不隨機怎麼辦?若是每次都往同一個塊內插入,複雜度沒法保證;
咱們能夠設定一個值,(我設爲當前序列數量的根號),一旦插入次數超過這個值,將整個序列從新分塊,理論複雜度O(M√N+N√M);
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,cnt,tot; int a[200010],bel[200010],st[200010],ed[200010]; int sum[200010]; int nxt[200010],pre[200010]; inline void miao() { int t=0; memset(sum,0,sizeof(sum)); cnt=sqrt(tot); for(int i=st[1];i;i=nxt[i]) { bel[i]=t/cnt+1; ++sum[bel[i]]; if(bel[i]^bel[pre[i]]) st[bel[i]]=i,ed[bel[i]-1]=pre[i]; ++t; } } inline void update(int x,int y) { int pos=0,now; for(int i=1;;++i) { if(pos+sum[i]>=x) { now=i; break; } pos+=sum[i]; } for(int i=st[now];i!=nxt[ed[now]];i=nxt[i]) { if(++pos==x) { a[++tot]=y; nxt[tot]=i; pre[tot]=pre[i]; nxt[pre[i]]=tot; pre[i]=tot; if(i==st[now]) st[now]=tot; ++sum[now]; break; } } } inline int query(int x) { int pos=0,now; for(int i=1;;++i) { if(pos+sum[i]>=x) { now=i; break; } pos+=sum[i]; } for(int i=st[now];i!=nxt[ed[now]];i=nxt[i]) { if(++pos==x) return a[i]; } } signed main() { n=tot=read(); cnt=sqrt(n); for(int i=1;i<=n;++i) { a[i]=read(); bel[i]=(i-1)/cnt+1; if(bel[i]^bel[i-1]) st[bel[i]]=i,ed[bel[i]-1]=i-1; ++sum[bel[i]]; if(i^n) nxt[i]=i+1; if(i^1) pre[i]=i-1; if(i==n) ed[bel[i]]=i; } for(int t,x,y,z,i=1;i<=n;++i) { t=read(),x=read(),y=read(),z=read(); if(!t) { update(x,y); if(tot%cnt==0) miao(); } else printf("%lld\n",query(y)); } return 0; }
分塊入門7:
區間乘法,區間加法,區間求和;(就是線段樹2嘛)
考慮兩種標記的前後順序:
若是咱們對於兩個同時存在的標記,先加再乘:
假設當前狀態爲(a+add)*mul;又添加了一組[add2,mul2]標記;
當前狀態變爲((a+add)*mul+add2)*mul2;
展開((a+add)*mul*mul2+add2*mul2);
展開(a*mul*mul2+add*mul*mul2+add2*mul2);
恢復標記格式:(a+add+(add2/mul))*mul*mul2;
發現了什麼?分數!這樣可能會丟精度qwq;
改變順序,先乘後加;
(a*mul+add)添加新標記[add2,mul2];
當前狀態變爲((a*mul)+add)*mul2+add2;
展開 (a*mul*mul2+add*mul2+add2);
恢復標記格式:(a*mul*mul2)+add*mul2+add2;
完美;
注意細節:在分塊中,若是要對散塊處理,要先把整個散塊的標記所有下放;
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } const int chx=10007; int n,cnt; int a[100010],bel[100010]; int add[100010],mul[100010]; inline void push_down(int x) { for(int i=(x-1)*cnt+1;i<=min(n,x*cnt);++i) { a[i] = ( a[i] * mul[x] + add[x] ) % chx ; } add[x]=0; mul[x]=1; } inline void upadd(int x,int y,int k) { int lx=bel[x],rx=bel[y]; push_down(lx); for(int i=x;i<=y&&bel[i]==lx;++i) { a[i] = ( a[i] + k ) % chx ; } if(lx==rx) return ; push_down(rx); for(int i=y;bel[i]==rx;--i) { a[i] = ( a[i] + k ) % chx ; } for(int i=lx+1;i<rx;++i) add[i] = ( add[i] + k ) % chx ; } inline void upmul(int x,int y,int k) { int lx=bel[x],rx=bel[y]; push_down(lx); for(int i=x;i<=y&&bel[i]==lx;++i) { a[i] = ( a[i] * k ) % chx ; } if(lx==rx) return ; push_down(rx); for(int i=y;bel[i]==rx;--i) { a[i] = ( a[i] * k ) % chx ; } for(int i=lx+1;i<rx;++i) { add[i] = ( add[i] * k ) % chx ; mul[i] = ( mul[i] * k ) % chx ; } } inline int query(int x) { return (( a[x] * mul[bel[x]] ) % chx + add[bel[x]] ) % chx ; } signed main() { n=read(); cnt=sqrt(n); for(int i=1;i<=n;++i) { a[i]=read()%chx; bel[i]=(i-1)/cnt+1; mul[bel[i]]=1; } for(int t,x,y,z,i=1;i<=n;++i) { t=read(),x=read(),y=read(),z=read(); if(!t) { upadd(x,y,z); } if(t==1) { upmul(x,y,z); } if(t==2) { printf("%lld\n",query(y)); } } return 0; }
分塊入門8:
查詢一段區間內元素等於k的個數,並將該區間內元素所有改成k;
一個想法是,將整塊的所有爲同一數字的打上標記,非同一數字:散塊暴力,整塊塊內排序二分查找;
很不幸,他T了……
正解我看了想噴人qwq
對於非同一數字部分暴力統計,同一數字部分整塊處理……
複雜度分析:對於原序列的改造:只要用O(n)的複雜度,必定會將原序列所有變成同一數字的;
那麼處理不一樣數字的代價是什麼呢?
考慮每一次操做,最多將兩個塊變成不一樣數字的,這兩個塊是須要暴力的,也就是說每次操做只會產生2個不一樣數字的塊,帶來√N的複雜度;
總複雜度爲O(M√N+N);
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,cnt; int a[100010],bel[100010],tag[100010]; inline void push_down(int x) { for(int i=(x-1)*cnt+1;i<=min(n,x*cnt);++i) a[i]=tag[x]; tag[x]=-1; } inline int query(int x,int y,int k) { int sum=0; int lx=bel[x],rx=bel[y]; if(tag[lx]!=-1) push_down(lx); for(int i=x;i<=y&&bel[i]==lx;++i) sum+=(a[i]==k),a[i]=k; if(lx==rx) return sum; if(tag[rx]!=-1) push_down(rx); for(int i=y;bel[i]==rx;--i) sum+=(a[i]==k),a[i]=k; for(int i=lx+1;i<rx;++i) { if(tag[i]!=-1) { sum+=(tag[i]==k?cnt:0); } else { for(int j=(i-1)*cnt+1;j<=i*cnt;++j) sum+=(a[j]==k); } tag[i]=k; } return sum; } signed main() { n=read(); cnt=sqrt(n); for(int i=1;i<=n;++i) { a[i]=read(); bel[i]=(i-1)/cnt+1; tag[i]=-1; } for(int x,y,z,i=1;i<=n;++i) { x=read(),y=read(),z=read(); printf("%lld\n",query(x,y,z)); } return 0; }
分塊入門9&&洛谷P4168蒲公英:
區間衆數查詢;
毒瘤來惹QAQ這道題目分享兩個方法:
方法一:二分
說點別的,明明最近有一次考試題目就是這道題的閹割版,只取二分部分就是切掉,我竟然沒想出來,哭惹哭惹;
有一個引理:有兩個數字集合A和B,設A集合的衆數爲X,那麼整個集合的衆數必定屬於X∪B;
證實:不會,詳見陳立傑《區間衆數解題報告》;
根據引理,咱們能夠知道:只要判斷兩個散塊的數和中間整塊的衆數就能夠找到衆數;
枚舉散塊和整塊衆數複雜度爲O(N),那麼咱們須要一個O(1)判斷一個數字在區間中出現次數的方法;
很遺憾,沒有,可是有logN的;
咱們能夠以下處理:
用vector數組存儲每一個數字出現的位置,用lower_bound和upper_bound找到這個數字在區間中第一次出現的位置和最後一次出現的位置;
至於整塊的衆數,能夠直接N√N處理出來,詳見代碼,一看就會qwq;
注意一個問題:在該題目中,因爲預處理只要一次,而查詢代價較多,咱們能夠適當減少塊的大小,讓每次查詢的代價更小;
親測當塊的大小爲50的時候跑地飛快;下面放的是洛谷AC代碼,LOJ上的題目需略做修改;
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,m,cnt,tot,kuai,miao; int lx,rx,maxsum,ans,t; int a[100010],bel[100010]; int f[1010][1010]; int val[100010]; int sum[100010]; vector<int> q[100010]; map<int,int> vis; inline void get_num(int x) { memset(sum,0,sizeof(sum)); int maxsum=-1,ans=0; for(int i=(x-1)*cnt+1;i<=n;++i) { ++sum[a[i]]; if(sum[a[i]]>maxsum||(sum[a[i]]>=maxsum&&val[a[i]]<=val[ans])) maxsum=sum[a[i]],ans=a[i]; f[x][bel[i]]=ans; } } inline int ask(int l,int r,int k) { return upper_bound(q[k].begin(),q[k].end(),r)-lower_bound(q[k].begin(),q[k].end(),l); } inline int query(int x,int y) { lx=bel[x],rx=bel[y]; maxsum=0,ans=0; for(int i=x;i<=y&&bel[i]==lx;++i) { t=ask(x,y,a[i]); if(t>maxsum||(t>=maxsum&&val[a[i]]<=val[ans])) maxsum=t,ans=a[i]; } if(lx^rx) { for(int i=y;bel[i]==rx;--i) { t=ask(x,y,a[i]); if(t>maxsum||(t>=maxsum&&val[a[i]]<=val[ans])) maxsum=t,ans=a[i]; } if(lx+1<=rx-1) { t=ask(x,y,f[lx+1][rx-1]); if(t>maxsum||(t>=maxsum&&val[f[lx+1][rx-1]]<=val[ans])) maxsum=t,ans=f[lx+1][rx-1]; } } return ans; } signed main() { n=read(),m=read(); cnt=50; for(int i=1;i<=n;++i) { a[i]=read(); if(!vis[a[i]]) { vis[a[i]]=++tot; val[tot]=a[i]; } a[i]=vis[a[i]]; q[a[i]].push_back(i); bel[i]=(i-1)/cnt+1; } for(int i=1;i<=bel[n];++i) get_num(i); for(int x,y,i=1;i<=m;++i) { x=read(),y=read(); x=(x+miao-1)%n+1,y=(y+miao-1)%n+1; if(x>y) swap(x,y); miao=val[query(x,y)]; printf("%lld\n",miao); } return 0; }
方法二:預處理:
預處理出一個p[ ][ ]數組,p[ i ][ j ]表示前i個塊內,j這個數字出現的次數;
表示這個方法也應該是N√N纔對,甚至理論複雜度更優秀,可是因爲一股神祕力量致使方法一將塊大小改成50後快得飛起,這個方法很不幸地慢了一截;
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,m,cnt,tot,kuai,miao; int lx,rx,maxsum,ans,t; int a[100010],bel[100010]; int f[1010][1010]; int p[500][40010]; int val[100010]; int sum[100010]; int c[100010]; inline void get_num(int x) { memset(sum,0,sizeof(sum)); int maxsum=-1,ans=0; for(int i=(x-1)*cnt+1;i<=n;++i) { ++sum[a[i]]; if(sum[a[i]]>maxsum||(sum[a[i]]>=maxsum&&val[a[i]]<=val[ans])) maxsum=sum[a[i]],ans=a[i]; f[x][bel[i]]=ans; } } inline int query(int x,int y) { lx=bel[x],rx=bel[y]; maxsum=0,ans=0; memset(sum,0,sizeof(sum)); for(int i=x;i<=y&&bel[i]==lx;++i) { ++sum[a[i]]; t=sum[a[i]]+max(p[rx-1][a[i]]-p[lx][a[i]],0ll); if(t>maxsum||(t>=maxsum&&val[a[i]]<=val[ans])) maxsum=t,ans=a[i]; } if(lx^rx) { for(int i=y;bel[i]==rx;--i) { ++sum[a[i]]; t=sum[a[i]]+max(p[rx-1][a[i]]-p[lx][a[i]],0ll); if(t>maxsum||(t>=maxsum&&val[a[i]]<=val[ans])) maxsum=t,ans=a[i]; } if(lx+1<=rx-1) { t=p[rx-1][f[lx+1][rx-1]]-p[lx][f[lx+1][rx-1]]+sum[f[lx+1][rx-1]]; if(t>maxsum||(t>=maxsum&&val[f[lx+1][rx-1]]<=val[ans])) maxsum=t,ans=f[lx+1][rx-1]; } } return ans; } signed main() { n=read(),m=read(); cnt=sqrt(n); for(int i=1;i<=n;++i) { a[i]=read(); bel[i]=(i-1)/cnt+1; c[++c[0]]=a[i]; } sort(c+1,c+c[0]+1); c[0]=unique(c+1,c+c[0]+1)-c-1; for(int i=1;i<=n;++i) { int tyx=lower_bound(c+1,c+c[0]+1,a[i])-c; val[tyx]=a[i]; a[i]=tyx; } for(int i=1;i<=bel[n];++i) { get_num(i); for(int j=(i-1)*cnt+1;j<=min(n,i*cnt);++j) { ++p[i][a[j]]; } for(int j=1;j<=c[0];++j) { p[i][j]+=p[i-1][j]; } } for(int x,y,i=1;i<=m;++i) { x=read(),y=read(); x=(x+miao-1)%n+1,y=(y+miao-1)%n+1; if(x>y) swap(x,y); miao=val[query(x,y)]; printf("%lld\n",miao); } return 0; }