树状数组

一. 树状数组简介:

  树状数组用来求区间元素的和,求一次区间元素和的效率为O(log n).

二. 图解树状数组:

  假设序列为:A[1]-A[8],C[]为树状数组.

  C[1] = A[1];

  C[2] = A[1] + A[2];

  C[3] = A[3];

  C[4] = A[1] + A[2] + A[3] + A[4];

  C[5] = A[5];

  C[6] = A[5] + A[6];

  C[7] = A[7];

  C[8] = A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8];

  将十进制化成二进制,然后观察这些二进制数最右边的1的位置:

  1 --> 00000001

  2 --> 00000010

  3 --> 00000011

  4 --> 00000100

  5 --> 00000101

  6 --> 00000110

  7 --> 00000111

  8 --> 00001000

  最右边的1的位置C[]的关系:

    在满二叉树中,

  以1结尾的节点(C[1], C[3], C[5], C[7]),其叶子数有1个,所以这些节点C[i]代表区间为1的元素和;

  以10结尾的节点(C[2], C[6]),其叶子数为2,所以这些节点C[i]代表区间范围为2的元素和;

  以100结尾的节点(C[4]),其叶子数为8个,锁业这些节点C[i]代表的区间范围为4的元素和;

  以1000结尾的节点(C[8]),其椰子树为8,所以这些节点C[i]代表的区间范围为8的元素和.

    扩展到一般情况:

  i的二进制中的从右往左数有连续的x个"0",那么拥有2^x个叶子,为序列A[]中的第 i - 2^x +1 到第 i 个元素的和.

  所以,C[i] = A[i-2^x+1] + ... + A[i],其中x为i的二进制中的从右往左数有连续"0"的个数.

  void init() {

    for (int i =1; i <= n; i++) {

      int len = i&(-i);

      while (len /= 2)

        C[i] += C[i-len];

      C[i] += A[i];

    }

  }

三. 利用树状数组求前i个元素的和S[i]:

    C[i] = A[i - 2^x + 1] + ... + A[i];

    int Sum(int i) {

      int s = 0;

      while (i > 0) {

        s += C[i];

        i -= i&(-i);

      }

      return  s;

    }

四. 更新C[]:

    void Update(int i, int value) {                                               //A[i]的改变值为value

      while (i <= n) {

        C[i] += value;

        i +=  i &(-i);

      }

    }

Reference:

[1] http://www.cnblogs.com/justforgl/archive/2012/07/27/2612364.html

原文地址:https://www.cnblogs.com/wubdut/p/4838526.html