菜鸡的 分块 刷题记录
洛谷P2801 教主的魔法
- 题意:区间加,然后查询区间内大于a的数.
- 做法:对于每个块维护一个它的排好序的序列,每次查找lowerbound就星,不是整块的就暴力。
然后注意每次加的时候不是整块的那部分原有顺序会被破坏,暴力再排序就是。
- 代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 #define nmax 1000005 3 4 using namespace std; 5 int a[nmax],b[nmax],p[nmax],add[nmax]={0}; 6 int n,bn,m,q; 7 8 inline void reset(int x){ //把块x的排序 9 int r=bn*x,l=bn*(x-1)+1; 10 for (int i=l; i<=r; i++) b[i]=a[i]; 11 sort(b+l,b+r+1); 12 } 13 14 void build(){ 15 cin>>n>>q; 16 bn=sqrt(n); 17 m=n/bn; 18 if(n%bn) m++; 19 for (int i=0; i<n; i++){ 20 scanf("%d",&a[i+1]); 21 p[i+1]=i/bn+1; 22 } 23 for (int i=1; i<=m; i++) reset(i); 24 } 25 26 inline int find(int l,int r,int x){ 27 int ans=0; 28 for (int i=l; i<=r; i++) if(a[i]>=x) ans++; 29 return ans; 30 } 31 32 inline void update(int l,int r,int x){ 33 if(p[l]==p[r]) { 34 for (int i=l; i<=r; i++) a[i]+=x; 35 reset(p[l]); 36 return; 37 } 38 for (int i=p[l]+1; i<p[r]; i++) add[i]+=x; 39 for (int i=l; i<=p[l]*bn; i++) a[i]+=x; 40 reset(p[l]); 41 for (int i=(p[r]-1)*bn+1; i<=r; i++) a[i]+=x; 42 reset(p[r]); 43 } 44 45 inline int query(int l,int r,int x){ 46 int ta,ans=0; 47 if(p[l]==p[r]) return find(l,r,x-add[ p[l] ]); 48 for (int i=p[l]+1; i<p[r]; i++) { 49 int r=bn*i,l=bn*(i-1)+1; 50 ta=lower_bound(b+l,b+r+1,x-add[i])-(b+l); 51 ans+=bn-ta; 52 } 53 ans+=find( l,p[l]*bn,x-add[ p[l] ]); 54 ans+=find( (p[r]-1)*bn+1 ,r,x-add[ p[r] ]); 55 return ans; 56 } 57 58 int main(){ 59 freopen("testdata.in","r",stdin); 60 build(); 61 char in[10]; 62 int l,r,t; 63 for (int i=0; i<q; i++) { 64 scanf("%s%d%d%d",in,&l,&r,&t); 65 if(in[0]=='M') update(l,r,t); 66 else printf("%d\n",query(l,r,t)); 67 } 68 return 0; 69 }
- 基本上照着hzwer学长打的,orz感谢这么好的分块教程。
BZOJ2120 数颜色
- 做法:用pre[i]表示下标为i的那个颜色它前一个同样颜色的下标,如果要统计l~r的颜色,则对于pre[i]>l的不计数,因为在l~r内这个颜色一定出现过不止一次了,然后就跟上面教主的魔法一样了~
- 注意:这道题能直接分块,修改的时候完全暴力,是因为有 “修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6” 这两个条件,不然就得换做法了quq。
- 代码:
1 #include <bits/stdc++.h> 2 #define nmax 10010 3 #define inf 1000010 4 5 using namespace std; 6 int n,q,m,bn; 7 int a[nmax],b[nmax],p[nmax],pre[nmax],idx[nmax*100]; 8 9 inline void reset(int x){ 10 int l=(x-1)*bn+1,r=x*bn; 11 for (int i=l; i<=r; i++) b[i]=pre[i]; 12 sort(b+l,b+r+1); 13 } 14 15 inline int find(int l,int r,int ll){ 16 int ans=0; 17 for (int i=l; i<=r; i++) if(pre[i]<ll) ans++; 18 return ans; 19 } 20 21 void build(){ 22 cin>>n>>q; 23 bn=sqrt(n); 24 m=n/bn; 25 if(n%bn) m++; 26 for (int i=1; i<=n; i++) { 27 scanf("%d",&a[i]); 28 int& t=idx[ a[i] ]; 29 pre[i]=t; 30 t=i; 31 p[i]=(i-1)/bn+1; 32 } 33 for (int i=n+1; i<=m*bn; i++) pre[i]=inf; 34 for (int i=1; i<=m; i++) reset(i); 35 } 36 37 inline void update(int c,int d){ 38 bool change=false; 39 for (int i=c-1; i>=1; i--) if(a[i]==d) { pre[c]=i; reset(p[c]); change=true; break; } 40 for (int i=c+1; i<=n; i++) if(a[i]==d) { pre[i]=c; reset(p[i]); break; } 41 for (int i=c+1; i<=n; i++) if(a[i]==a[c]) {pre[i]=pre[c]; reset(p[i]); break;} 42 if(!change) pre[c]=0; reset(p[c]); 43 a[c]=d; 44 } 45 46 inline int query(int l,int r){ 47 int ans=0; 48 if(p[l]==p[r]) ans=find(l,r,l); 49 else{ 50 for (int i=p[l]+1; i<p[r]; i++) { 51 int tl=(i-1)*bn+1,tr=i*bn; 52 int t=lower_bound(b+tl,b+tr+1,l)-(b+tl); 53 ans+=t; 54 } 55 ans+=find(l,p[l]*bn,l); 56 ans+=find( (p[r]-1)*bn+1 ,r,l); 57 } 58 return ans; 59 } 60 61 int main(){ 62 build(); 63 char in[10]; 64 int c,d; 65 for (int i=0; i<q; i++) { 66 scanf("%s%d%d",in,&c,&d); 67 if(in[0]=='Q') printf("%d\n",query(c,d)); 68 else update(c,d); 69 } 70 return 0; 71 }
BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊
- 这题用LCT就很难(因为不会),但是用分块就是煞笔题
- 维护每个点跳出自己那个块需要的次数和跳到的地方就ok了,见代码
- 代码:
1 #include <bits/stdc++.h> 2 #define nmax 400020 3 4 using namespace std; 5 int a[nmax],pos[nmax],jump[nmax],cnt[nmax]; 6 //jump[i]表示点跳出i那个块跳到的地方 7 //cnt[i]表示跳出i那个块需要的次数 8 int n,m,bn,next,q; 9 10 inline void work(int idx){ 11 next=idx+a[idx]; 12 cnt[idx]=1; 13 jump[idx]=next; 14 if(pos[next]==pos[idx]) { cnt[idx]+=cnt[next]; jump[idx]=jump[next]; } 15 } 16 17 void build(){ 18 bn=sqrt(n); 19 m=n/bn; 20 if(n%bn) m++; 21 for (int i=0; i<n; i++) pos[i]=(i/bn); 22 for (int i=n; i<n+2*bn; i++) pos[i]=m+1; 23 for (int j=n-1; j>=0; j--) work(j); 24 } 25 26 void change(int p,int x){ 27 int l=pos[p]*bn; 28 a[p]=x; 29 for (int i=p; i>=l; i--) work(i); 30 } 31 32 int solve(int p){ 33 int ans=0; 34 while(p<n){ 35 ans+=cnt[p]; 36 p=jump[p]; 37 } 38 return ans; 39 } 40 41 int main(){ 42 cin>>n; 43 for (int i=0; i<n; i++) scanf("%d",&a[i]); 44 build(); 45 cin>>q; 46 int ina,inb,inc; 47 while(q--){ 48 scanf("%d%d",&ina,&inb); 49 if(ina==1) printf("%d\n",solve(inb)); 50 else{ 51 scanf("%d",&inc); 52 change(inb,inc); 53 } 54 } 55 return 0; 56 }