树状数组
习题
首先:
要说树状数组,就必须要说 $\operatorname{lowbit}$。
那 $\operatorname{lowbit}$ 是什么呢?
是一个数的二进制从右往左第一个 $1$ 的值。
例如,$\operatorname{lowbit}(14)$ 是 $2$,$\operatorname{lowbit}(16)$ 是 $16$。
那应该怎么求呢?
是 $n \And (-n)$,众所周知 $-n$ 是 $n$ 的补码,就是 $n$ 按位取反再加 $1$。
神奇的是,$n$ 这样一取反前面的 $0$ 都成 $1$ 了,再加一,$1$ 又变成 $0$ 了,但取反后第一个 $0$ 就成 $1$ 了,竟跟取反前一模一样!
这样一来,只要第一个 $1$ 剩下来了。
我们在举个栗子:
$6$ 的二进制是 $110$,补码是 $010$,$\operatorname{lowbit}(6)$ 就是 $2$。
$24$ 的二进制是 $11000$,补码是 $01000$,$\operatorname{lowbit}(6)$ 就是 $8$。
之后(进入正题):
我们有一个数列(随便写的):
$$a{8}={1,1,4,3,7,9,2,5}$$
再写一个表,每个数为下面的两数之和:
32
9 23
2 7 16 7
1 1 4 3 7 9 2 5
我们要实现的是:
- 单点修改。
- 询问区间。
但我们可以做出优化:
我们发现,如下的点会被用过
32
9
2 16
1 4 7 2
标号:
$~~~~~~~~~~~~~~~~~~~t_8$
32
$~~~~~~~~~t_4$
9
$~~~~~~~~~~~~~t_6$t_2
2 16
$t_1t_3t_5~~~~~~t_7$
1 4 7 2
显然:若 $i$ 为奇数,$t_i$ 等于 $a_i$。
观察可得 $t_i=a_i+a_{i+1}+…+a_{\operatorname{lowbit}(i)+i-1}$。
所以每次跳过 $\operatorname{lowbit}(i)$ 即可。
建树:
每次都把包含 $x$ 的区间(即每次跳过 $\operatorname{lowbit}(x)$)加上 $k$。
1 | void add(int x,int k){ |
查询:
与上面同理。
1 | int sum(int x){ |
码字不易,求赞