一般树状数组
单点更改, 区间查询
1 | class FenwickTree: |
例题
差分树状数组
区间更改, 单点查询
1 | class DiffFenwickTree: |
例题
双树状数组
水群知道的, 比较有趣的知识.
支持区间修改区间查询
1 | class RURQFenwickTree: |
例题
证明
差分数组 $b_i$ 和原数组 $a_i$ 的关系
在代码中, $a_i$ 是原数组, 而 $b_i$ 是它的差分数组, 定义为:
$$ b_i = a_i - a_{i-1} $$
这样,原数组 $a_i$ 可以表示为差分数组 $b_i$ 的前缀和:
$$ a_i = \sum_{j=1}^{i} b_j $$
区间和 sum(a[l:r])
的推导
现在考虑计算区间 [l, r]
的和:
$$ \sum_{i=l}^{r} a_i = \sum_{i=l}^{r} \sum_{j=1}^{i} b_j $$
这个双重求和可以重新排列:
$$ \begin{aligned} \sum_{i=l}^{r} \sum_{j=1}^{i} b_j &= \sum_{j=1}^{r} b_j \cdot (r - j + 1) - \sum_{j=1}^{l-1} b_j \cdot (l - j) \end{aligned} $$
对于以上操作, 更直观的理解, 考虑由$b_j$组成的”三角形”, 由上倒下分别:
$$ \begin{array}{c} &b_1 \cr &b_1, b_2 \cr &\vdots \cr &b_1, b_2, \cdots, b_n \end{array} $$
可以发现, 期间有$n$个$b_1$, $n-1$个$b_2$直到一个$b_n$. 所以式子可以如此转化:
$$ \sum_{i=1}^{n} \sum_{j=1}^{i} b_j = \sum_{j=1}^{n} b_j \cdot (n - j + 1)$$
当只需要$l \to r$时, 相当于大三角形减去小三角形:
$$ \begin{array}{c} &b_1, \cdots, b_l \cr &b_1, b_2, \cdots, b_{l+1} \cr &\vdots \cr &b_1, b_2, b_3, \cdots, b_n \end{array} $$
可以得到:
$$ \sum_{i=l}^{r} \sum_{j=1}^{i} b_j = \sum_{i=1}^{r} \sum_{j=1}^{i} b_j - \sum_{i=1}^{l-1} \sum_{j=1}^{i} b_j = \sum_{j=1}^{r} b_j \cdot (r - j + 1) - \sum_{j=1}^{l-1} b_j \cdot (l - j) $$
使用树状数组优化计算
为了高效计算这个和, 代码使用了两个树状数组:
tr1[i]
: 维护差分数组 $b_i$ 的前缀和(即sum(b[1:i])
)tr2[i]
: 维护i * b[i]
的前缀和(即sum(j * b[j] for j in range(1,i+1)
)
这样, 区间和可以表示为:
$$ \sum_{i=l}^{r} a_i = \text{query}(r) - \text{query}(l-1) $$
其中 query(p)
计算的是:
$$ \text{query}(p) = (p + 1) \cdot \text{sum}(b[1:p]) - \text{sum}(j \cdot b[j] \text{ for j in range(1, p+1)}) $$
即:
$$ \text{query}(p) = (p + 1) \cdot tr1[p] - tr2[p] $$
到此, 我们完成了区间查询的证明.
我很可爱,请给我钱
- 本文链接: https://www.zh314.xyz/2025/05/21/Python-树状数组/
- 版权声明: 本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。