树状数组能够很容易求出前缀和

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 }
POJ3648

相关文章: