[bzoj 5143][Ynoi 2018]五彩斑斕的世界
阿新 • • 發佈:2018-12-27
傳送門
Descroption
給了你一個長為n的序列a,有m次操作
1.把區間[l,r]中大於x的數減去x
2.查詢區間[l,r]中x的出現次數
Solution
分塊
對於每個塊,我們都分別維護:
每種數出現的次數,需要用到並查集,即把相同的數合併在一起
- 標記tag,表示這個塊總共減了多少
maxx,表示這個塊的最大值
對於查詢,按照分塊的套路直接查詢就可以了
修改的時候,為了保證複雜度正確,我們考慮每次通過$O ( x ) \(的複雜度,實現整個塊的極差減少\)x$.
這樣,我們就能保證總修改的複雜度是\(O(n\log n)\)了
怎麼做到呢?
假設當前陣列的極差為\(M\),我們近似的將它看成是\(maxx-tag\)
- \(M< x\) 顯然是不存在大於x的數,直接返回即可
- \(x \leq M \leq 2x\) 這個時候,把大於x的數直接減去x即可,
fa[j]=fa[j-x]
,這樣極差減少\(x\)- \(M>2x\) 直接把小於等於\(x\)的數加上\(x\),然後整個區間打上減\(x\)的標記,極差同樣減少\(x\)
Code
#include<bits/stdc++.h> #define ll long long using namespace std; char B[1<<26],*S=B; inline int read(){ int x;char c; while((c=*S++)<'0'||c>'9'); for(x=c-'0';(c=*S++)>='0'&&c<='9';)x=x*10+c-'0'; return x; } const int MN=1e5,T=650; int a[MN+5],fa[MN/T+5][MN+5],sz[MN/T+5][MN+5],minn[T+5],maxx[T+5]; int getf(int*f,int k){return f[k]?f[k]=getf(f,f[k]):k;} int main() { B[fread(B,1,1<<26,stdin)]=0; int n,m,i,j,o,l,r,x,lk,rk,ans; n=read();m=read(); for(i=1;i<=n;++i)a[i]=read(),++sz[(i-1)/T][a[i]]; for(i=1;i<=n;i+=T) minn[i/T]=0,maxx[i/T]=MN; while(m--) { o=read();l=read();r=read();x=read(); lk=(l-1)/T;rk=(r-1)/T; if(o==1) { for(i=l;i<=r&&i<=lk*T+T;++i) if((a[i]=getf(fa[lk],a[i]))-minn[lk]>x)--sz[lk][a[i]],++sz[lk][a[i]-=x]; for(i=lk;++i<rk;) if(maxx[i]-minn[i]>2*x) { for(j=1;j<=x;++j)sz[i][fa[i][minn[i]+j]=minn[i]+j+x]+=sz[i][minn[i]+j]; minn[i]+=x; } else { for(j=x+1;j<=maxx[i]-minn[i];++j)sz[i][fa[i][minn[i]+j]=minn[i]+j-x]+=sz[i][minn[i]+j]; maxx[i]=min(maxx[i],minn[i]+x); } if(lk!=rk)for(i=r;i>rk*T;--i) if((a[i]=getf(fa[rk],a[i]))-minn[rk]>x)--sz[rk][a[i]],++sz[rk][a[i]-=x]; } else { ans=0; for(i=l;i<=r&&i<=lk*T+T;++i)if(getf(fa[lk],a[i])-minn[lk]==x)++ans; for(i=lk;++i<rk;)if(x+minn[i]<=maxx[i])ans+=sz[i][x+minn[i]]; if(lk!=rk)for(i=r;i>rk*T;--i)if(getf(fa[rk],a[i])-minn[rk]==x)++ans; printf("%d\n",ans); } } }
Blog來自PaperCloud,未經允許,請勿轉載,TKS!