目录
右图圆圈中标记有数字的结点,存储的是称为树状数组的 tree[]tree[]。一个结点上的 tree[]tree[]的值,就是它树下的直连的子结点的和。例如:
- tree[1]=a1
- tree[2]=tree[1]+a2
- tree[3]=a3
- tree[4]=tree[2]+tree[3]+a4
- ⋯
- tree[8]=tree[4]+tree[6]+tree[7]+a8
而我们利用 tree[],有效地完成下面两个操作:
-
查询,即求前缀和 sum,例如:
- sum(8)=tree[8]
- sum(7)=tree[7]+tree[6]+tree[4]
- sum(6)=tree[6]+tree[4]
右图中的虚线箭头是计算 sum(7) 的过程。显然,计算的复杂度是 O(logn) 的,这样就达到了快速计算前缀和的目的。
-
维护。tree[] 本身的维护也是高效的。当元素 a 发生改变时,能以 O(logn)的高效率修改tree[] 的值。例如更新了 a3,那么只需要修改 tree[3]、tree[4]、tree[8]⋯,即修改它和它上面的那些结点:父结点以及父结点的父结点。
- 查询的过程,是每次去掉二进制的最后的 1。例如求 sum(7) = tree[7] + tree[6] + tree[4],步骤是:
- 7 的二进制是111,去掉最后的 1,得 110,即 tree[6];
- 去掉 6 的二进制 110 的最后一个 1,得 100,即tree[4];
- 4 的二进制是 100,去掉 1 之后就没有了。
- 维护的过程,是每次在二进制的最后的 1 上加 1。例如更新了a3,需要修改tree[3]、tree[4]、tree[8]⋯ 等等,步骤是:
- 3 的二进制是 11,在最后的 1 上加上 1 得100 ,即 4 ,修改 tree[4];
- 4 的二进制是 100,在最后的 1 上加 1,得 1000,即 8,修改 tree[8];
- 继续修改 tree[16]、tree[32]⋯ 等等。
“去掉二进制的最后的 1”、“在二进制的最后的 1
lowbit(x) = x & -x
的功能是找到 x 的二进制数的最后一个 1。其原理是利用了负数的补码表示,补码是原码取反加一。例如
- x = 6 =
- -x = x补 =
- 得
lowbit(x) = x & -x
=
1.动态求连续区间和
给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b][a,b] 的连续和。
输入格式
第一行