zoukankan      html  css  js  c++  java
  • 树状数组

    树状数组

    本来以为特别难呢,没想到一点都不难。

    图大致就是这样,是不是很像一个树。。。

    a数组是正常输入的数组,c数组就是要求的tree。

    c[1]=a[1];c[2]=c[1]+a[2];

    c[3]=a[3];c[4]=c[2]+c[3]+a[4];

    c[5]=a[5];c[6]=c[5]+a[6];

    c[7]=a[7];c[8]=c[4]+c[6]+c[7]+a[8];

    而树状数组一般只有两种操作:

    1.将某个数增加一个值。

    2.查询某个区间的所有值的和。

    我们会发现把某一个数的值增加后,之后和它相连所有点的值都要加,那怎么实现呢?

    这时候就看出二进制的牛逼之处了,那就是使用lowbit。

    例如:

    把节点1增加一个值hahaha后,相应的,节点2,节点4,节点8都要增加。这时,我们使用lowbit就可以了。

    lowbit(x)=x&-x;

    1的二进制是0001,-1就是1111(一个数加一个负号是把这个数的二进制取反+1),他俩&一下,还是0001,也就是1。原来的1再加上求得的1,结果就是2。(这个例子不太成功,继续哈)

    2的二进制是0010,-2就是1110,他俩&一下,还是0010,也就是2。原来的2再加上求得的2,结果就是4。(还是不行,再继续)

    4的二进制是0100,-4就是1100,他俩&一下,还是0100,也就是4。原来的4再加上求得的4,结果就是8。(。。。。。。)

    貌似整个例子都这样了,那就再随便来一个了:
    7的二进制是0111,-7就是1001,他俩&一下,就是0001,也就是1。原来的7加上求得的1,结果是8,就是要增加的下一个节点啦。(终于对了)

    所以,代码附上:

    int add(int s,int num){
        while(s<=n){
            tree[s]+=num;
            s+=s&-s;
        }
    }

    然后是查询,其实也差不多,只不过是把原来的反过来就好了。原来是加上lowbit,现在改成减,往回推就好了。

    查询一个区间的代码:

    int read(int x,int y){
        x--;
        sum1=0;
        sum2=0;
        while(x){
            sum1+=tree[x];
            x-=x&-x;
        }
        while(y){
            sum2+=tree[y];
            y-=y&-y;
        }
        return sum2-sum1;
    }

    洛谷有两道树状数组的模板题:树状数组1树状数组2

    第一道题比较正常:

    1.将第x个数加上k。

    2.输出区间[x,y]内每个数的和。

    代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 int n,m,a,tree[500001],b,xx,yy,sum1,sum2;
     5 int add(int s,int num){
     6     while(s<=n){
     7         tree[s]+=num;
     8         s+=s&-s;
     9     }
    10 }
    11 int read(int x,int y){
    12     x--;
    13     sum1=0;
    14     sum2=0;
    15     while(x){
    16         sum1+=tree[x];
    17         x-=x&-x;
    18     }
    19     while(y){
    20         sum2+=tree[y];
    21         y-=y&-y;
    22     }
    23     printf("%d
    ",sum2-sum1);
    24 }
    25 int main(){
    26     scanf("%d%d",&n,&m);
    27     for(int i=1;i<=n;++i){
    28         scanf("%d",&a);
    29         add(i,a);
    30     }
    31     for(int i=1;i<=m;++i){
    32         scanf("%d%d%d",&b,&xx,&yy);
    33         if(b==1)
    34             add(xx,yy);
    35         else
    36             read(xx,yy);
    37     }
    38     return 0;
    39 }
    View Code

    第二道题就恶心了:

    1.将区间[x,y]内每个数加上k。

    2.输出第x个数的值。

    于是,我们就可以通过让每个数的之前所有区间的所有数的和代表这个数的值。(语文太差,没治了)

    用实例解释一下:

    第8个节点的值就是c[8]。第7个节点的值就是c[4]+c[6]+c[7]。

    第6个节点的值就是c[4]+c[6]。第5个节点的值就是c[4]+c[5]。

    第4个节点的值就是c[4]。第3个节点的值就是c[2]+c[3]。

    第2个节点的值就是c[2]。第1个节点的值就是c[1]。

    所以代码就变成了:

     1 #include<cstdio>
     2 #define lowbit(x) x&-x
     3 int n,m,sum,x,y,z;
     4 int c[500001];
     5 void add(int x,int d){
     6     while(x<=n){
     7         c[x]+=d;
     8         x+=x&-x;
     9     }
    10 }
    11 int read(int x){
    12     sum=0;
    13     while(x){
    14         sum+=c[x];
    15         x-=x&-x;
    16     }
    17     printf("%d
    ",sum);
    18 }
    19 int main(){
    20     long long k;
    21     scanf("%d%d",&n,&m);
    22     for(int i=1;i<=n;i++){
    23         y=x;
    24         scanf("%d",&x);
    25         add(i,x-y);
    26     }
    27     for(int i=1;i<=m;i++){
    28         scanf("%d",&z);
    29         if(z==1){
    30             scanf("%d%d%d",&x,&y,&k);
    31             add(x,k);
    32             add(y+1,-k);
    33         }
    34         else{
    35             scanf("%d",&x);
    36             read(x);
    37         }
    38     }
    39     return 0;
    40 }
    View Code

    第一次写这么长的博客,累死。

    另外,这位的博客写的不错,我也是通过这篇博客学会树状数组的。

  • 相关阅读:
    docker安装与使用路径
    python3.7简单的爬虫
    ubuntu19.04下查看软件安装目录和详细信息
    Javascript检查对象是否存在某个属性
    bootstrap源码和测试
    python学习笔记之pdb调试
    pydensecrf安装报错1、UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 29: invalid start byte2、 LINK : fatal error LNK1158: 无法运行“rc.exe” error: command 'D:\software\vs2015\VC\BIN
    python学习日记:np.newaxis
    好用的网址集锦
    网络配置ipconfig /release、ipconfig /renew
  • 原文地址:https://www.cnblogs.com/jsawz/p/6745151.html
Copyright © 2011-2022 走看看