树状数组 重温
upd.on 2025.7.5 修复一些表述错误
详解
对于单点修改,前缀查询一类问题。我们如何 logn 的时间维护?
无非就是拆区间。可以区间折半,乃线段树。
也可以拆位,考虑把区间拆成多个二进制下的区间,也能做到 logn 的时间,这就是树状数组。
比如 21 的前缀和,拆成 10101。也就是
$s[0, 21]=s[21-2^{0}, 21] + s[21-2^0-2^2, 21-2^0] + s[21-2^0-2^2-2^4, 21-2^0-2^2]$
$s[0, 21]=s[20, 21] + s[16, 20] + s[0, 16]$
这里拆 s 的过程其实是每次都去掉末尾二进制的 1,我们可以用 lowbit 获取末尾二进制的 1,实现不断拆 s。
lowbit:为了取到末尾1,需要每一位取反后加 1 ,这样低位全为 0,高位和原数全部不一样。此时再和原数 与 一下即可。相当于与上原数的负数。
(特别注意一点,lowbit(0)=0,代码实现时要注意死循环问题)
由此,我们引入 $tr$ 数组记录二进制下的各个区间信息。
也就是:tr[u] 管理 [u-lowbit(u), u] 的区间和。
这样相当于把 s 拆成若干个 tr,组成若干条链,最终构成了树形结构图。

一个性质:u 的父亲是 u+lowbit(u),是容易推的。
单点修改:根据上述性质,修改 u 只会对其父亲有影响,不断改父亲即可。也就是不断加上 lowbit(u)
前缀查询:就是拆 s 的过程,不断拆即可,也就是不断减去 lowbit(s)。
板子
[一维] 单点加 区间查
很板,把区间用前缀表示即可。记录详情 - 洛谷 | 计算机科学教育新生态
树状数组的本质就是单点加,前缀查!
[一维] 区间加 单点查
区间加,结合树状数组刚好是维护前缀和,所以可以转换成差分实现。那单点查就是查前缀和,直接查就好。
[一维] 区间加 区间查
区间加维护差分即可,区间查呢?推下式子。
区间查询:令 $sum(1, R)$ 表示 $\sum_{i=1}^R a_i$
由差分,$sum(x, x)=\sum_{i=1}^x tr_i$
先考虑一段的情况,即
$sum(1, R)=\sum_{i=1}^R \sum_{j=1}^i tr_j$
再展开上式得 $\sum_{i=1}^Rtr_i \times (R-i+1)$(可以画图理,即序列当前数 i 只会被 i 后面的数加到)
上式的求和里跟 $R$ 有关比较难算,美化下,让求和里尽量只和 $i$ 有关,得 $R \times \sum_{i=1}^Rtr_i - \sum_{i=1}^Rtr_i \times (i-1)$
好看多了,让 $tr2_R$ 维护 $\sum_{i=1}^Rtr_i \times (i-1)$ 就做完了。
[二维] 单点加 区间查
就是多了一维嘛,tr[m][n],相当于 m 组一维的 tr[n]。但是对于新一维 m 也可以同样处理方法,加减 lowbit 快速管理。
区间查就类似二位前缀和即可。
[二维] 区间加 单点查
同样维护二维差分即可。
[二维] 区间加 区间查
令 $sum(a, b, c, d)$ 表示 $(a, b)$ 到 $(c, d)$ 的和。
$sum(1, 1, a, b)=\sum_{i=1}^a \sum_{j=1}^b \sum_{k=1}^i \sum_{u=1}^j tr_{k ,u}$
展开,$\sum_{i=1}^a \sum_{j=1}^b tr_{i ,j} \times (a-i+1) \times (b-j+1)$(可以画图理,即矩阵当前数 [i, j] 只会被 [i, j] 到 [a, b] 矩阵里的数加到)
再展开,$\sum_{i=1}^a \sum_{j=1}^b tr_{i ,j} \times (ab-aj+a-bi+ij-i+b-j+1)$
优美下,尽可能让 $tr_{i, j}$ 与 $i$,$j$ 有关,把 $a$,$b$ 提到求和前面处理,即按 $i$ 和 $j$ 提式子:
$\sum_{i=1}^a \sum_{j=1}^b tr_{i ,j} \times (ij-i(b+1)-j(a+1)+(ab+a+b+1))$
所以分开维护四个式子即可:
1. $\sum_{i=1}^a \sum_{j=1}^b tr_{i ,j} \times i \times j$
2. $(b+1) \times \sum_{i=1}^a \sum_{j=1}^b tr_{i ,j} \times i$
3. $(a+1) \times \sum_{i=1}^a \sum_{j=1}^b tr_{i ,j} \times j$
4. $(ab+a+b+1) \times \sum_{i=1}^a \sum_{j=1}^b tr_{i ,j} $
例题
[简化+容斥思想] Loj10115
1.简化问题:因为区间内元素互不相同,我们只需要关心区间端点,从而对左右端点分别建树状数组。
2.容斥。直接求难,用全部方案-不合法。且这里的全部方案比较巧妙。
[dfs 序] CF383C
Submission #327664514 - Codeforces
1.跟子树操作有关,一定要想到 dfs 序,且注意把树拍成序列后,树上的点编号和序列下标要对应好。
2.val, -val, -(-val),得想到奇偶相关,这样就能分离奇偶,方便处理。

浙公网安备 33010602011771号