树状数组

Posted aiahtwo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组相关的知识,希望对你有一定的参考价值。

详细介绍:https://www.cnblogs.com/xenny/p/9739600.html

http://www.sohu.com/a/245746824_100201031

https://www.cnblogs.com/wkfvawl/p/9445376.html

单点更新、单点查询

传统数组可做

1.单点修改+区间查询

int n;//点的数量
int a[50005],c[50005]; //对应原数组和树状数组
int lowbit(int x)
    return x&(-x);

void updata(int i,int k)    //在i位置加上k
    while(i <= n)
        c[i] += k;
        i += lowbit(i);
    

//求sum(x)就是a[x]的前缀和,想查询l~r区间的元素和只需要求出来sum(r)-sum(l-1)
int getsum(int i)
    int res = 0;
    while(i > 0)
        res += c[i];
        i -= lowbit(i);
    
    return res;

int main()

memset(a,0,sizeof(a));
memset(c,0,sizeof(c));

 

模板题:

技术图片
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
//单点修改加区间查询
using namespace std;
int n;
int a[50005],c[50005]; //对应原数组和树状数组
int lowbit(int x)
    return x&(-x);

void updata(int i,int k)    //在i位置加上k
    while(i <= n)
        c[i] += k;
        i += lowbit(i);
    

//求sum(x)就是a[x]的前缀和,想查询l~r区间的元素和只需要求出来sum(r)-sum(l-1)
int getsum(int i)
    int res = 0;
    while(i > 0)
        res += c[i];
        i -= lowbit(i);
    
    return res;

int main()

    int T;
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
    
        scanf("%d",&n);
        memset(a, 0, sizeof a);
        memset(c, 0, sizeof c);
        for(int i=1;i<=n;i++)
        
            scanf("%d",&a[i]);
            updata(i,a[i]);
        
        printf("Case %d:\\n",i);
        char s[10];
        while(cin>>s)
        if(s[0]==Q)//区间查询
        int l,r;
        scanf("%d%d",&l,&r);
        cout<<getsum(r)-getsum(l-1)<<endl;
        
        if(s[0]==A)//单点增加  第x个数加t
            int x,t;
            scanf("%d%d",&x,&t);
            updata(x,t);
        
        if(s[0]==S)//单点减少  第x个数减t
            int x,t;
            scanf("%d%d",&x,&t);
            updata(x,-1*t);
        
        if(s[0]==E)//结束
            break;
        
    
HDU - 1166

2.区间修改+单点查询

int n,m;
int a[50005] = 0,c[50005]; //对应原数组和树状数组

int lowbit(int x)
    return x&(-x);


void updata(int i,int k)    //在i位置加上k
    while(i <= n)
        c[i] += k;
        i += lowbit(i);
    


int getsum(int i)        //求D[1 - i]的和,即A[i]值
    int res = 0;
    while(i > 0)
        res += c[i];
        i -= lowbit(i);
    
    return res;


int main()
    cin>>n;

     memset(a,0,sizeof(a));
     memset(c,0,sizeof(c));

     for(int i = 1; i <= n; i++)
        cin>>a[i];
        updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
    
    
    //[x,y]区间内加上k
    updata(x,k);    //A[x] - A[x-1]增加k
    updata(y+1,-k);        //A[y+1] - A[y]减少k
    
    //查询i位置的值
    int sum = getsum(i);

    return 0;

例题:P3368 【模板】树状数组 2           https://www.luogu.org/problem/show?pid=3368   

技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 //单点修改加区间查询
 7 using namespace std;
 8 int n,m;
 9 int a[500005] = 0,c[500005]; //对应原数组和树状数组
10 
11 int lowbit(int x)
12     return x&(-x);
13 
14 
15 void updata(int i,int k)    //在i位置加上k
16     while(i <= n)
17         c[i] += k;
18         i += lowbit(i);
19     
20 
21 
22 int getsum(int i)        //求D[1 - i]的和,即A[i]值
23     int res = 0;
24     while(i > 0)
25         res += c[i];
26         i -= lowbit(i);
27     
28     return res;
29 
30 
31 int main()
32     int m;
33     cin>>n>>m;
34     memset(a,0,sizeof(a));
35     memset(c,0,sizeof(c));
36     for(int i = 1; i <= n; i++)
37     cin>>a[i];
38     updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
39     
40     for(int i=1;i<=m;i++)
41     
42         int t;
43         cin>>t;
44         if(t==1)
45         
46             int x,y,k;
47             cin>>x>>y>>k;
48              //[x,y]区间内加上k
49            updata(x,k);    //A[x] - A[x-1]增加k
50            updata(y+1,-k);        //A[y+1] - A[y]减少k
51         
52         else
53         
54             int kk;
55             cin>>kk;
56             //查询i位置的值
57            int sum = getsum(kk);
58            cout<<sum<<endl;
59         
60     
61     return 0;
62 
View Code

 

3.区间修改+区间查询

int n,m;
int a[50005] = 0;
int sum1[50005];    //(D[1] + D[2] + ... + D[n])
int sum2[50005];    //(1*D[1] + 2*D[2] + ... + n*D[n])

int lowbit(int x)
    return x&(-x);


void updata(int i,int k)
    int x = i;    //因为x不变,所以得先保存i值
    while(i <= n)
        sum1[i] += k;
        sum2[i] += k * (x-1);
        i += lowbit(i);
    


int getsum(int i)        //求前缀和
    int res = 0, x = i;
    while(i > 0)
        res += x * sum1[i] - sum2[i];
        i -= lowbit(i);
    
    return res;


int main()
    cin>>n;
    for(int i = 1; i <= n; i++)
        cin>>a[i];
        updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
    

    //[x,y]区间内加上k
    updata(x,k);    //A[x] - A[x-1]增加k
    updata(y+1,-k);        //A[y+1] - A[y]减少k

    //求[x,y]区间和
    int sum = getsum(y) - getsum(x-1);

    return 0;

例题:https://vjudge.net/problem/POJ-3468

技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 //单点修改加区间查询
 7 using namespace std;
 8 #define ll long long
 9 int n,m;
10 ll a[100005] = 0;
11 ll sum1[100005];    //(D[1] + D[2] + ... + D[n])
12 ll sum2[100005];    //(1*D[1] + 2*D[2] + ... + n*D[n])
13 
14 int lowbit(int x)
15     return x&(-x);
16 
17 
18 void updata(int i,ll k)
19     int x = i;    //因为x不变,所以得先保存i值
20     while(i <= n)
21         sum1[i] += k;
22         sum2[i] += k * (x-1);
23         i += lowbit(i);
24     
25 
26 
27 ll getsum(int i)        //求前缀和
28     ll res = 0, x = i;
29     while(i > 0)
30         res += x * sum1[i] - sum2[i];
31         i -= lowbit(i);
32     
33     return res;
34 
35 
36 int main()
37     int Q;
38     ios_base::sync_with_stdio(false);
39 cin.tie(0);
40 cout.tie(0);
41     cin>>n>>Q;
42     for(int i = 1; i <= n; i++)
43         cin>>a[i];
44         updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
45     
46 
47     for(int i=1;i<=Q;i++)
48     
49         char c;
50         cin>>c;
51         if(c==Q)
52         
53             int x,y;
54             cin>>x>>y;
55             long long sum = getsum(y) - getsum(x-1);
56             cout<<sum<<endl;
57         
58         else
59         
60             int x,y;
61             ll k;
62             cin>>x>>y>>k;
63              //[x,y]区间内加上k
64             updata(x,k);    //A[x] - A[x-1]增加k
65             updata(y+1,-k);        //A[y+1] - A[y]减少k
66         
67     
68     return 0;
69 
View Code

 

以上是关于树状数组的主要内容,如果未能解决你的问题,请参考以下文章

树状数组的树状数组的经典操作

树状数组模板

数据结构之树状数组从零认识树状数组

树状数组 / 二维树状数组

浅谈树状数组

Bit的树状数组