树状数组模板题 (区间查询+区间修改)
我们知道,线段树实现区间修改和查询可以通过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;
}