分块入门
将长为n操作序列分成好几块
设定每块大小为根号n,这样就把序列分为了n/根号n块
对于每一块,整体处理,不能构成一块的,枚举处理
比如线段树的4个基本操作
4个数组,bl[]记录属于哪一块,a[]记录原值,sum_sum[]记录块内总和,sum_change[]记录这一块都怎么改
单点查询i:直接输出a[i]
单点修改(如果是加上某个数)i:既修改a[i],又要修改sum_sum[bl[i]]
区间修改[l,r](整体加上x):分3步
① x所在块,但要注意是从l到min(l所在块的最后一个,y)
枚举,a[i]+x,sum_sum[bl[i]]+x
② x和y之间的块(不包括x,y所在块)
一次性更改整个快,既sum_change[bl[i]]+x
③ y所在块,但要注意前提是x和y不在同一块内,
枚举 a[i]+x,sum_sum[bl[i]]+x
区间查询(求和):也是分3步 分步情况与上面一样,不在叙述
① ans+=a[i]+sum_change[bl[i]]
② ans+=sum_sum[i]+sum_change[i]*根号n
③ ans+=a[i]+sum_change[bl[i]]
即将答案分为2部分,中间在块内的、两边不能构成一块的
以下3题练练手
T1 http://codevs.cn/problem/1080/ 线段树练习 单点修改+区间查询
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cmath> #include<algorithm> #define N 100001 using namespace std; int n,m,t; int sum[350],a[N],bl[N]; int query(int x,int y) { int cur=0; for(int i=x;i<=min(bl[x]*t,y);i++) cur+=a[i]; for(int i=bl[x]+1;i<bl[y];i++) cur+=sum[i]; if(bl[x]!=bl[y]) for(int i=(bl[y]-1)*t+1;i<=y;i++) cur+=a[i]; return cur; } int main() { scanf("%d",&n); t=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); bl[i]=(i-1)/t+1; sum[bl[i]]+=a[i]; } scanf("%d",&m); int x,y,z; for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); if(x==1) { a[y]+=z; sum[bl[y]]+=z; } else printf("%d\n",query(y,z)); } }
T2 http://codevs.cn/problem/1081/ 线段树练习2 单点查询+区间修改
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cmath> #include<algorithm> #define N 100001 using namespace std; int n,m,t; int sum[350],a[N],bl[N]; int main() { scanf("%d",&n); t=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); bl[i]=(i-1)/t+1; } scanf("%d",&m); int x,y,z,p; for(int k=1;k<=m;k++) { scanf("%d",&p); if(p==1) { scanf("%d%d%d",&x,&y,&z); for(int i=x;i<=min(y,bl[x]*t);i++) a[i]+=z; for(int i=bl[x]+1;i<bl[y];i++) sum[i]+=z; if(bl[x]!=bl[y]) for(int i=(bl[y]-1)*t+1;i<=y;i++) a[i]+=z; } else { scanf("%d",&x); printf("%d\n",a[x]+sum[bl[x]]); } } }
T3 http://codevs.cn/problem/1082/ 线段树练习3 区间修改+区间查询
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cmath> #include<algorithm> #define N 200001 using namespace std; int n,m,t,bl[N]; long long sum_change[500],a[N],sum_sum[500]; int main() { scanf("%d",&n); t=sqrt(n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); bl[i]=(i-1)/t+1; sum_sum[bl[i]]+=a[i]; } scanf("%d",&m); int x,y,z,p; for(int k=1;k<=m;k++) { scanf("%d",&p); if(p==1) { scanf("%d%d%d",&x,&y,&z); for(int i=x;i<=min(y,bl[x]*t);i++) a[i]+=z,sum_sum[bl[i]]+=z; for(int i=bl[x]+1;i<bl[y];i++) sum_change[i]+=z; if(bl[x]!=bl[y]) for(int i=(bl[y]-1)*t+1;i<=y;i++) a[i]+=z,sum_sum[bl[i]]+=z; } else { long long cur=0; scanf("%d%d",&x,&y); for(int i=x;i<=min(y,bl[x]*t);i++) cur+=a[i]+sum_change[bl[i]]; for(int i=bl[x]+1;i<bl[y];i++) cur+=sum_sum[i]+sum_change[i]*t; if(bl[x]!=bl[y]) for(int i=(bl[y]-1)*t+1;i<=y;i++) cur+=a[i]+sum_change[bl[i]]; printf("%lld\n",cur); } } }