树状数组 BIT 总结

· · 个人记录

树状数组 BIT 总结

0x00 功能

树状数组,即BIT,用于在 \mathcal{O}(\log n) 的时间复杂度下处理有结合律的前缀查询,以及单点修改。

0x01 实现

下面讲解都以前缀和为例

定义

使用树状结构对每一个区间使用二进制进行划分,如下图:

对于节点 i,1\leq i\leq size 定义其父亲节点为 i+\operatorname{lowbit}\left(i\right) ,并且定义这个节点的权值为 \operatorname{val}_i=\left(\sum_{j\in son_i}val_j\right)+arr_i 。

其中

推论

求解

查询

假设现在我们需要求前缀和到 x 这个点,设

1. $ans\longleftarrow ans+val_x$ 即将 $\left[x-\operatorname{lowbit}\left(x\right)+1,x\right]$ 这个区间的值。 2. $x\longleftarrow x-\operatorname{lowbit}\left(x\right)$ 即将 $x$ 偏移到没去的区间。 3. 如果 $x$ 不为 $0$ 则 `goto 1` 。 这样就可以把大前缀区间拆成若干个小区间进行求和。 Code: ```cpp int presum(int x) { int ans = 0; while (x > 0) { ans = ans + c[x]; x = x - lowbit(x); } return ans; } ``` #### 修改 假设要将 $x$ 这个点的值加上 $k$ ,只需要把这个节点的所有父亲加上 $k$ 即可。 Code: ```cpp void add(int x, int k) { while (x <= n) { c[x] = c[x] + k; x = x + lowbit(x); } } ``` ### 时间复杂度 #### 查询 由于一直减去 $\operatorname{lowbit}\left(x\right)$ ,而每次减去会去掉末尾的 $1$ 那么有多少个 $1$ 就会减去多少次,即 $\operatorname{popcount}\left(x\right)$ 次。显然 $\mathcal{O}\left(\operatorname{popcount}\left(n\right)\right)=\mathcal{O}\left(\log n\right)

修改

同理一直加上 \operatorname{lowbit}\left(x\right) ,而每次加显然也会去掉末尾的 1 显然 \mathcal{O}\left(\log n\right)

0x02 Tricks

单点修改 区间查询

刚才实现的都是 单点修改 前缀查询 ,若这个运算具有差分性质,就可以实现 单点修改 区间查询 。

直接维护差分数组即可。

\mathcal{O}\left(n\right) 建树

建树朴素方法就是每个点加上 arr_i 这样建树方法就是 \mathcal{O}\left(n\log n\right) 。但是实际上可以

考虑从儿子节点向上贡献每一个节点的值是由所有与自己直接相连的儿子的值求和得到的。因此可以倒着考虑贡献,即每次确定完儿子的值后,用自己的值更新自己的直接父亲。 Code: ```cpp // Θ(n) 建树 void init() { for (int i = 1; i <= n; ++i) { t[i] += a[i]; int j = i + lowbit(i); if (j <= n) t[j] += t[i]; } } ``` ### $\operatorname{lowbit}\left(x\right)$ 实现细节 利用二进制补码特性 $\operatorname{lowbit}\left(x\right)=x\&-x$ 。 将 $x$ 的二进制所有位全部取反,再加 `1` ,就可以得到 `-x` 的二进制编码。例如,`6` 的二进制编码是 `110` ,全部取反后得到 `001` ,加 `1` 得到 `010` 。 设原先 `x` 的二进制编码是 `(...)10...00` ,全部取反后得到 `[...]01...11` ,加 `1` 后得到 `[...]10...00`,也就是 `-x` 的二进制编码了。这里 `x` 二进制表示中第一个 `1` 是 `x` 最低位的 `1` 。 `(...)` 和 `[...]` 中省略号的每一位分别相反,所以 `x & -x = (...)10...00 & [...]10...00 = 10...00` ,得到的结果就是 lowbit。 ### Others 诸如二维BIT,不需要差分的BIT等等不太重要的Trick。可以查看 [树状数组 - OI Wiki](https://oi-wiki.org/ds/fenwick/) ## 谢鸣 本文部分图片/内容/代码摘自 [树状数组 - OI Wiki](https://oi-wiki.org/ds/fenwick/)