树状数组学习笔记

来源:互联网 发布:怎么在知乎提问 编辑:程序博客网 时间:2024/06/06 08:58

理论

注意,这篇文章中,除解释了的,其它数字都表示都是二进制下的数字。

lowbit

  • 对于二进制数 x,lowbit(x)=2^k,其中 k 为 x 末尾 0 的个数。
  • 即除掉”从右到左第一个 1 左边的数”,剩下的就是它的 lowbit 值。
    • lowbit(00000000)=0
    • lowbit(00000001)=1
    • lowbit(01110101)=1
    • lowbit(00001010)=10
    • lowbit(01010110)=10
    • lowbit(01001000)=1000
    • lowbit(10101000)=1000
  • 要求 x 的 lowbit 值,可以利用二进制编码的特性。

    lowbit(x)=x&(-x) 
  • 解释
    • -x 为 x 的补码,等于反码+1
    • 反码中,全部位取反,那么最后那一坨0变成了1,而最右边那个1变成了0;
    • 补码反码再+1,则原来最右边为 1 的那一位会被进位成 1,右边又变回 0,左边的位都取反了。
    • 那么 原码补码之后,只有 lowbit 值的只有原码最右边那个 1 的那位相同,得到的就是 lowbit 值了。
    • 例子:
      • 原码:10101000
      • 反码:01010111
      • 补码:01011000
      • 与值:00001000 (即为lowbit值)

c[]

  • 对于数列 a[],建立树状数组 c[]
  • c[x]=a[x-lowbit(x)+1]+...+a[x](从左边那个累加到右边那个)
    • c[100101]=a[100101]+…+a[100101]
    • c[100100]=a[100001]+…+a[100100]
    • c[100000]=a[000000]+…+a[100000]
    • c[101100]=a[101001]+…+a[101100]

插入

  • a[x] 需要加 y ,那么可以这么操作对树状数组(c[])做修改,n 为 a[] 的大小(元素个数)。
void plus(int x,int y){    while (x<=n){        c[x]+=y;        x+=lowbit(x);    }}
  • 这应该是显然的,所有包含 a[x] 的 树状数组 中的节点都应该加上 y,而上面这个方法就能枚举出所有包含 a[x] 的节点。
  • 因为包含 a[x] 的节点c[k]只需满足以下条件:lowbit值小于lowbit(x),且不小于x。
    • 例子x=10110010,则需修改的节点为以下
      • 10110010 (10110001~10110010)
      • 10110100 (10110001~10110100)
      • 10101000 (10100001~10101000)
      • 10110000 (10100000~10110000)
      • 11000000 (10000000~11000000)

查询

  • 一次查询本身只能求前缀,而可以通过前缀相减求得区间信息。
  • 求前 x 个数的的和可以这样操作:
int query(int x){    int ret=0;    while (x>0){        ret+=c[x];        x-=lowbit(x);    }    return ret;}
  • 这个的正确性也可以自己举几个例子来理解:
    • 例子x=10100
      • a[1]+...+a[10100]=
      • c[10100] (10001~10100)+
      • c[10000] (00001~10000)
  • 后来我又想到一个说法,要是求前 x 个的和,可以把 1~x 看成一条线段
  • 这里写图片描述
  • 如图,灰色是原线段,那么将它“二进制化”(类似于十进制转二进制那样一位位除下来把它分成二的幂长度的若干段(每种长度最多出现一次)),每一段代表每一位,就成了个二进制数。
  • 比如途中红色是绿色的四分之一,蓝色是红色的四分之一,要是蓝色长度为10,那么整一段的长度则可以表示为 10101 (换成十进制的话就是22,你们可以算算,长度的确是蓝色的22倍)
  • 可以看出每一段对应着二进制表示中的一位 1
  • 设左边为 0,那么每一段右端点代表的数的 c[] 值就是那一段元素的和。
  • 每次累加当前 xc 值就是累加最右边那一段的元素,然后再x-=lowbit(x)就是删去最右边那段。
  • 这样以此次操作下来,每一段都被加了之后被删掉,轮到左边相邻那一段,最后加到的和就是整段 a[1]~a[x] 的和了。
  • 不知道这样能不能理解。(有点倍增的味道)

参考文章(http://blog.csdn.net/howardemily/article/details/54974385),感觉还是很好的,帮助很大,建议看一看它,感觉比我的更全面,易懂。

原创粉丝点击