树状数组模板题 (区间查询+区间修改)

我们知道,线段树实现区间修改和查询可以通过lazy标记来实现

今天学到一个新姿势,树状数组也可以实现区间修改和查询

我们引入d数组delta[i]表示区间[i, n]的共同增量 于是修改区间[l, r]时修改d[l]和d[r + 1]即可 (就是差分的思路)
查询的时候是查询区间 [l, r] 的和 即sum[r] - sum[l - 1] 所以现在的问题是求sum[i]


sum[i] = a[1]+...+a[i] + d[1]*i + d[2]*(i - 1) + d[3]*(i - 2)+...+d[i]*1   // a[i]为原始数组
        = sigma( a[x] ) + sigma( d[x]  *  (i + 1 - x) )
       = sigma( a[x] ) + (i + 1) * sigma( d[x] ) - sigma( d[x] * x )

1
2
3
其中 sigma( a[x] ) 是可以预处理出来的 于是只需要维护 d[x] 与 d[x] * x 的前缀和(作为两个树状数组就可以了)
下面是一道板子题

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int maxn=1e5+5;
int n,m,x,y,v;
LL ans;
LL c1[maxn],c2[maxn];
int a[maxn];
char c;
int lowbit(int x){
	return x&(-x);
}
void update(int x,LL v){
	for(int i=x;i<=n;i+=lowbit(i)){
		c1[i]+=v;
		c2[i]+=x*v;
	}
}
LL query(int x){
	LL ANS=0;
	for(int i=x;i>0;i-=lowbit(i)){
		ANS+=(x+1)*c1[i]-c2[i];
	}
	return ANS;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		update(i,a[i]-a[i-1]);
	}
	while(m--){
		cin>>c; 
		if(c=='Q'){
			scanf("%d%d",&x,&y);
			ans=query(y)-query(x-1);
			printf("%lld\n",ans);
		}
		if(c=='C'){
			scanf("%d%d%d",&x,&y,&v);
			update(x,v);update(y+1,-v);
		}
	}
	return 0;
}

posted @ 2018-11-19 17:44  Dzzzzzz  阅读(261)  评论(0编辑  收藏  举报