分块
闲的没事就学了学...
概况:
分块,顾名思义,就是把一段序列分成一小块一小块得来处理,维护。
我们把一段当成一个整体,只记录维护整体的有关信息,就是分块。
实现:
直接在代码里解释吧...
//a[N] 输入的数组 //pos[N] 元素所在的区域 //sum[N] 一个区域的元素总和 //add[N] 记录对这个区间的修改(有点像lazy_tag) //L[N] 每个区间的左端点 //R[N] 每个区间的右端点
初始化操作:
cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } int t=sqrt(n);//分块的大小 for(int i=1;i<=t;i++){ L[i]=(i-1)*t+1; R[i]=i*t;//初始化边界大小 } if(R[t]<n){//如果剩下了一些元素,把剩下的元素合并成一个块 t++; L[t]=t*t+1; R[t]=n; } for(int i=1;i<=t;i++){ for(int j=L[i];j<=R[i];j++){ pos[j]=i;//每个元素所在块的编号 sum[i]+=a[j];//每个块的元素总和 } }
添加操作:
void modify(int l,int r,int k){ int p=pos[l]; int q=pos[r]; if(p==q){//如果修改区间在一个块内 for(int i=l;i<=r;i++) a[i]+=k; sum[p]+=(l-r+1)*k;//直接修改每个元素和总和 } else{//不在一个块内 for(int i=p+1;i<=q-1;i++) add[i]+=k;//用add[]记录在其中的块的变化 for(int i=l;i<=R[p];i++) a[i]+=k; sum[p]+=(R[p]-l+1)*k; for(int i=L[q];i<=r;i++) a[i]+=k; sum[q]+=(r-L[q]+1)*k;//剩下的左边一点和右边一点操作同在一个区间的情况 } }
修改操作:
int ask(int l,int r){ int ans=0; int p=pos[l]; int q=pos[r]; if(p==q){ for(int i=l;i<=r;i++) ans+=a[i]; ans+=add[p]*(r-l+1);//在一个区间直接找答案 } else{ for(int i=p+1;i<=q-1;i++) ans=ans+sum[i]+add[i]*(R[i]-L[i]+1);//统计整块区间答案 for(int i=l;i<=R[p];i++) ans+=a[i]; ans+=add[p]*(R[p]-l+1); for(int i=L[q];i<=r;i++) ans+=a[i]; ans+=add[q]*(r-L[q]+1);//处理左边和右边的零碎区间 } return ans; }
可以发现,分块其实就是把大区间操作整体实现,小区间就直接朴素地进行操作。
总结为八字:
大段维护,局部朴素。
其实就是优雅的暴力。
模板Code:
#include<bits/stdc++.h> using namespace std; const int N=101; int a[N],pos[N],sum[N],add[N]; int L[N],R[N]; int n,m; int op; int ll,rr,k; void modify(int l,int r,int k){ int p=pos[l]; int q=pos[r]; if(p==q){ for(int i=l;i<=r;i++) a[i]+=k; sum[p]+=(l-r+1)*k; } else{ for(int i=p+1;i<=q-1;i++) add[i]+=k; for(int i=l;i<=R[p];i++) a[i]+=k; sum[p]+=(R[p]-l+1)*k; for(int i=L[q];i<=r;i++) a[i]+=k; sum[q]+=(r-L[q]+1)*k; } } int ask(int l,int r){ int ans=0; int p=pos[l]; int q=pos[r]; if(p==q){ for(int i=l;i<=r;i++) ans+=a[i]; ans+=add[p]*(r-l+1); } else{ for(int i=p+1;i<=q-1;i++) ans=ans+sum[i]+add[i]*(R[i]-L[i]+1); for(int i=l;i<=R[p];i++) ans+=a[i]; ans+=add[p]*(R[p]-l+1); for(int i=L[q];i<=r;i++) ans+=a[i]; ans+=add[q]*(r-L[q]+1); } return ans; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } int t=sqrt(n); for(int i=1;i<=t;i++){ L[i]=(i-1)*t+1; R[i]=i*t; } if(R[t]<n){ t++; L[t]=t*t+1; R[t]=n; } for(int i=1;i<=t;i++){ for(int j=L[i];j<=R[i];j++){ pos[j]=i; sum[i]+=a[j]; } } for(int i=1;i<=m;i++){ cin>>op; if(op==1){ cin>>ll>>rr>>k; modify(ll,rr,k); } if(op==2){ cin>>ll>>rr; cout<<ask(ll,rr); } } return 0; }