树状数组能够很容易求出前缀和
1.对于区间更新问题
我们要借用辅助函数来帮助自己
自定义一个函数add[i] , 表示将i~n的数都加了add[i]的值
那么将[s,t]区间都增加v,就很容易理解为add[s]+=v , add[t+1]-=v了
查询区间[s,t]的总和的时候就可以理解为:
ans = sum(t) - sum(s-1)
这里只要考虑sum(x)怎么求解即可
比如原来前i个数保存在a[]中
那么
这就很容易理解为是用3个树状数组求一个a[i],一个add[i],一个i*add[i]的前缀和了
当然这里也可以把a[i]和-add[i]*i合并在一起求前缀和~
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 #define N 100010 5 #define lowbit(x) (x&(-x)) 6 #define ll long long 7 ll sum[N] , add[N] , sub[N]; 8 int n,q; 9 void initAdd(int p , int v) 10 { 11 for(int x=p ; x<=n ; x+=lowbit(x)) sum[x]=sum[x]+v; 12 } 13 void update(int s , int t , int v) 14 { 15 for(int x=s ; x<=n ; x+=lowbit(x)){ 16 add[x]+=v; 17 sub[x]+=(ll)s*v; 18 } 19 for(int x=t+1 ; x<=n ; x+=lowbit(x)){ 20 add[x]-=v; 21 sub[x]-=(ll)v*(t+1); 22 } 23 } 24 ll query(int s , int t) 25 { 26 ll ans = 0; 27 for(int x=t ; x>0 ; x-=lowbit(x)) ans+=sum[x]+(ll)(t+1)*add[x]-sub[x]; 28 for(int x=s-1 ; x>0 ; x-=lowbit(x)) ans-=sum[x]+(ll)s*add[x]-sub[x]; 29 return ans; 30 } 31 int main() 32 { 33 // freopen("a.in" , "r" , stdin); 34 while(~scanf("%d%d" , &n , &q)){ 35 memset(sum,0,sizeof(ll)*(n+1)); 36 memset(add,0,sizeof(ll)*(n+1)); 37 memset(sub,0,sizeof(ll)*(n+1)); 38 int x; 39 for(int i=1; i<=n ; i++){ 40 scanf("%d" , &x); 41 initAdd(i,x); 42 } 43 char op[2]; 44 while(q--){ 45 scanf("%s" , op); 46 int a,b,c; 47 if(op[0]=='Q'){ 48 scanf("%d%d" , &a , &b); 49 printf("%I64d\n" , query(a,b)); 50 }else{ 51 scanf("%d%d%d" , &a , &b , &c); 52 update(a , b , c); 53 } 54 } 55 } 56 return 0; 57 }