树状数组

参考了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

最后一个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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值