我的感受這中間的三題是最水的沒有之一git
這個也是不少數據結構完爆的題目線段樹入門題,可是練分塊咱們就要寫嗎數組
修改仍是與以前相似,只不過咱們要維護每一塊內元素的和,注意這個要實時更新數據結構
這樣就能夠輕鬆水過了。函數
CODEui
#include<cstdio> #include<cctype> #include<cmath> using namespace std; const int N=50005,BLO=250; int n,a[N],blk[N],size,opt,x,y,z; long long mark[BLO],sum[BLO]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1; while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline int min(int a,int b) { return a<b?a:b; } inline int query(int l,int r,int mod) { register int i; int res=0; for (i=l;i<=min(blk[l]*size,r);++i) res=(res+1LL*(a[i]+mark[blk[l]]))%mod; if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) res=(res+1LL*(a[i]+mark[blk[r]]))%mod; for (i=blk[l]+1;i<=blk[r]-1;++i) res=(res+1LL*sum[i])%mod; return res; } inline void modify(int l,int r,int x) { register int i; for (i=l;i<=min(blk[l]*size,r);++i) a[i]+=x,sum[blk[l]]+=x; if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) a[i]+=x,sum[blk[r]]+=x; for (i=blk[l]+1;i<=blk[r]-1;++i) mark[i]+=x,sum[i]+=x*size; } int main() { //freopen("4.in","r",stdin); freopen("4.out","w",stdout); register int i; read(n); size=(int)sqrt(n); for (i=1;i<=n;++i) read(a[i]),sum[blk[i]=(i-1)/size+1]+=a[i]; for (i=1;i<=n;++i) { read(opt); read(x); read(y); read(z); if (opt) write(query(x,y,z+1)),putchar('\n'); else modify(x,y,z); } return 0; }
這道題其實也是一道經典的並查集的題目,可是咱們只講分塊。spa
首先咱們要注意到,一個數被進行開方操做至多\(O(log^2n)\)次時它就會變成\(0/1\)code
而後咱們對於每個塊在維護和的同時,在打上一個標記,當一個塊內全部元素都是\(0/1\)時就不更新它。it
不然暴力搞一遍便可。最後注意修改時記得把區間和的標記一塊兒維護不能直接開方io
CODE入門
#include<cstdio> #include<cctype> #include<cmath> using namespace std; const int N=50005,BLO=250; int n,a[N],blk[N],sum[BLO],size,opt,x,y,z; bool flag[BLO]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1; while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline int min(int a,int b) { return a<b?a:b; } inline void reset(int x) { register int i; flag[x]=1; for (i=(x-1)*size+1;i<=x*size;++i) { sum[x]-=a[i]; a[i]=sqrt(a[i]); sum[x]+=a[i]; if (a[i]>1) flag[x]=0; } } inline int query(int l,int r) { register int i; int res=0; for (i=l;i<=min(blk[l]*size,r);++i) res+=a[i]; if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) res+=a[i]; for (i=blk[l]+1;i<=blk[r]-1;++i) res+=sum[i]; return res; } inline void modify(int l,int r) { register int i; for (i=l;i<=min(blk[l]*size,r);++i) sum[blk[l]]-=a[i],a[i]=sqrt(a[i]),sum[blk[l]]+=a[i]; if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) sum[blk[r]]-=a[i],a[i]=sqrt(a[i]),sum[blk[r]]+=a[i]; for (i=blk[l]+1;i<=blk[r]-1;++i) if (!flag[i]) reset(i); } int main() { //freopen("5.in","r",stdin); freopen("5.out","w",stdout); register int i; read(n); size=sqrt(n); for (i=1;i<=n;++i) read(a[i]),sum[blk[i]=(i-1)/size+1]+=a[i]; for (i=1;i<=n;++i) { read(opt); read(x); read(y); read(z); if (opt) write(query(x,y)),putchar('\n'); else modify(x,y); } return 0; }
首先要正確理解題意,而後咱們發現這個和分塊有個毛線關係。
咱們注意到對於使用數組模擬這個過程時,查詢是\(O(1)\)的,但在同時插入時是\(O(n)\)的
那麼咱們思考如何權衡這個問題,咱們也能夠進行分塊。
只不過這裏的分塊就是對於每塊內開一個vector,而後你要知道有一個insert函數真是超級好用
而後咱們在插入時能夠作到\(O(\sqrt n)\),在數據隨機的狀況下表現優異。
CODE
#include<cstdio> #include<cctype> #include<cmath> #include<vector> #define pb push_back using namespace std; const int N=200005,BLO=450; int n,size,opt,x,y,z,tot; vector <int> r[BLO]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1; while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline int min(int a,int b) { return a<b?a:b; } inline int query(int x) { for (register int i=1;i<=tot;++i) if (x<=r[i].size()) return r[i][x-1]; else x-=r[i].size(); } inline void insert(int x,int y) { for (register int i=1;i<=tot;++i) if (x<=r[i].size()) { r[i].insert(r[i].begin()+x-1,y); return; } else x-=r[i].size(); } int main() { //freopen("a.in","r",stdin); freopen("a.out","w",stdout); register int i; read(n); size=sqrt(n); tot=(n-1)/size+1; for (i=1;i<=n;++i) read(x),r[(i-1)/size+1].pb(x); for (i=1;i<=n;++i) { read(opt); read(x); read(y); read(z); if (opt) write(query(y)),putchar('\n'); else insert(x,y); } return 0; }
不過上面的作法也有必定的缺陷,若是出題人就是要卡你,只須要一直出在同一位置插入的數據便可。
而後最壞狀況下就變成\(O(n^2)\)暴力了,而後咱們引進從新分塊這樣的概念
一種重構相似於替罪羊樹式重構,當一個塊內的元素特別多(能夠設臨界值)時,直接暴力把這個塊裂成兩半便可。
可是我更喜歡一個超級粗暴的方式:每作\(\sqrt n\)次操做後,直接從新分塊(所有for過去),複雜度和大致的一致,也是\(O(n\sqrt n)\)的。
這樣就卡不了你了可是在隨機數據下被不重構的分塊吊打了
CODE
#include<cstdio> #include<cctype> #include<cmath> #include<vector> #define pb push_back using namespace std; const int N=200005,BLO=450; int n,size,opt,cur[N],x,y,z,tot,q; vector <int> r[BLO]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1; while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline int min(int a,int b) { return a<b?a:b; } inline void rebuild(void) { register int i,j; n=0; for (i=1;i<=tot;++i) { for (j=0;j<r[i].size();++j) cur[++n]=r[i][j]; r[i].clear(); } size=sqrt(n); tot=(n-1)/size+1; for (i=1;i<=n;++i) r[(i-1)/size+1].pb(cur[i]); } inline int query(int x) { for (register int i=1;i<=tot;++i) if (x<=r[i].size()) return r[i][x-1]; else x-=r[i].size(); } inline void insert(int x,int y) { for (register int i=1;i<=tot;++i) if (x<=r[i].size()) { r[i].insert(r[i].begin()+x-1,y); return; } else x-=r[i].size(); } int main() { //freopen("6.in","r",stdin); freopen("6.out","w",stdout); register int i; read(n); size=sqrt(n); tot=(n-1)/size+1; for (i=1;i<=n;++i) read(x),r[(i-1)/size+1].pb(x); for (q=n,i=1;i<=q;++i) { read(opt); read(x); read(y); read(z); if (opt) write(query(y)),putchar('\n'); else insert(x,y); if (i%size==0) rebuild(); } return 0; }