说到维护动态区间求和(不管最大值最小值),那树状数组真的能比线段树简单很多。
看了一位学姐的总结真的收获好大(^-^)V
(1)首先树状数组的单点修改+区间查询 应该都会吧。即
update(l,x);
res=query(r)-query(l-1)
update和query这里就略过了,后面会有。
(2)区间修改+单点查询
其实跟(3)差不多,也就是维护一个delta数组用差分解决
(3)区间修改+区间查询
这个真的真的很有意思,以前我根本就不知道qwq,但现在发现好简单w。
我们用delta[i]表示i..n的共同增量。
那么利用差分思想在修改的时候只要
delta[l]+=x;
delta[r+1]-=x;
即可,重点就是在求和的地方
ans[i]=a[1]+a[2]+...+a[i]+delta[1]*i+delta[2]*(i-1)+...+delta[i]*1
=sum(a[j])+sum(delta[j]*(i-j+1))
=sum(a[j])+(i+1)*sum(delta[j])-sum(delta[j]*j)
for each 1<=j<=i
这样应该就显而易见了,sum(a[j])用前缀和维护,而sum(delta[j])和sum(delta[j]*j)用两个树状数组维护即可。
这样纸树状数组是不是很简单O(∩_∩)O
推荐去做做codeVS1082 以前用线段树打一脸懵逼的WA qwq
这里就附上区间修改+区间查询的代码吧,真心比线段树简单好多~
/*
作者:Leo
题目:p1082 线段树练习 3
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
#define N 200000+2000
using namespace std;
int n,Q;ll delta1[N],delta2[N],sum[N];
int read(){
int x=0; char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x;
}
inline int lowbit(int x){return x & (-x);}
inline int update1(int i,int x){
while(i<=n){
delta1[i]+=x;
i+=lowbit(i);
}
}
inline int update2(int i,int x){
while(i<=n){
delta2[i]+=x;
i+=lowbit(i);
}
}
ll query1(int i){
ll res=0;
while(i>0){
res+=delta1[i];
i-=lowbit(i);
}
return res;
}
ll query2(int i){
ll res=0;
while(i>0){
res+=delta2[i];
i-=lowbit(i);
}
return res;
}
int main()
{
n=read();
for(int i=1;i<=n;i++){
int x=read();
sum[i]=sum[i-1]+x;
}
Q=read();
while(Q--){
int opt=read();
if(opt==1){
int l=read(),r=read(),x=read();
update1(l,x);update1(r+1,-x);
update2(l,x*l);update2(r+1,-x*(r+1));
}else{
int l=read(),r=read();
ll sum1=sum[l-1]+l*query1(l-1)-query2(l-1);
ll sum2=sum[r]+(r+1)*query1(r)-query2(r);
printf("%lld\n",sum2-sum1);
}
}
return 0;
}