参考了http://baike.baidu.com/item/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84
原数组:a[], 树状数组:c[];
1.最重要的概念:c[n] = a[n] + a[n-1] + a[n-2] +...+ a[n-2^k-1]. k是n的二进制末尾0的个数。
c[n] 的物理意义是从n到n的二进制下最后一个1所覆盖的元素的和。(i不断的减一,使最后一个1和后边的所有的0,变成最后一个1变0,后边的0又都变回0,除了末尾位是1之外)
举例:第一个例子: c[6].
6 的二进制为 1 1 0
1 1 0 = 6
1 0 1 = 5
c[6] = a[6]+a[5];
第二个例子: c[4].
4 的二进制位 1 0 0
1 0 0 = 4
0 1 1 = 3
0 1 1 = 2
0 0 1 = 1
c[4] = a[4] + a[3] + a[2] + a[1]
设计这样的c数组的好处是,可以像把从1到一个整数i区间的数组和a[1...i] 像把i二进制分解一样快速求出,复杂度为O(logn).
求和:
求和的过程: 从末位开始,看见1,就把这个一所覆盖的元素的和累加的结果res中,直到所有的1都算过了,求和就结束了。
例如求sum(1~6) :
6的二进制 1 1 0
最后一个1覆盖的元素和保存在c[6] 中(a[6] + a[5]), res+=c[6]
最后一个1算完了,把这个1减掉,二进制剩 1 0 0
再算新的最后一个1,最后一个1覆盖的元素和保存在c[4]中(a[4]+a[3]+a[2]+a[1]) res+=c[4]
算完了这个1,就没有1了,也就算完了。 res中的结果正是a[1] +...+a[6]
如果生成c[]:
每更新一个元素时,我们假设更新下标为i的元素,那么包含这个元素的c数组都应该更新相同的量(+delta, 或-delta)。那哪些c[x] 需要修改呢?
首先显然c[i]要修改,因为a[i] 时c[i]中最大的那个元素。
然后因为c[x] 为x二进制最后一的10...0变0...01的所有元素,中间“扫”过的元素变化c[x]就应该变化,
同时我们发现i的二进制最后一个1前面的任何0都是c[x]中x二进制最后那个10...0变为0...01的那个 0. 我们不断的在i上加二进制的i的最后那个10...0就可得x。
举个例子:
a[6] = a[6]+1。
6的二进制是 1 1 0
最后一个 10...0 是 1 0. 那么
1 1 0
+ 0 1 0
----------------------
0 0 1 0 0 0
即x = 8, 所有c[8] = c[8] + 1。 同时8又是一个改变的元素,对于x>8的x, 需要继续上面的过程直到 maxn
具体实现CPP:
int lowbit(int x) { return x&-x; }
void add(int i, int x) {
while (i <= maxn) {
c[i] += x;
i += lowbit(i);
}
}
int sum(int i) {
int res = 0;
while (i) {
res += c[i];
i -= lowbit(i);
}
return res;
}