Python 树状数组
算法 | python | 数据结构 | 模板
2025年5月21日
一般树状数组
单点更改, 区间查询
class FenwickTree:
def __init__(self, size):
self.size = size
self.tree = [0] * (self.size + 1)
def update(self, index, delta):
while index <= self.size:
self.tree[index] += delta
index += index & -index
def query_prefix(self, index):
res = 0
while index > 0:
res += self.tree[index]
index -= index & -index
return res
def query(self, index):
return self.query_range(index, index)
def query_range(self, left, right):
return self.query_prefix(right) - self.query_prefix(left - 1)
例题
差分树状数组
区间更改, 单点查询
class DiffFenwickTree:
def __init__(self, size):
self.size = size
self.tree = [0] * (self.size + 1)
def _update(self, index, delta):
while index <= self.size:
self.tree[index] += delta
index += index & -index
def update(self, index, delta):
self._update(index, delta)
self._update(index + 1, -delta)
def update_range(self, start, end, delta):
self._update(start, delta)
self._update(end + 1, -delta)
def query(self, index):
res = 0
while index > 0:
res += self.tree[index]
index -= index & -index
return res
例题
双树状数组
水群知道的, 比较有趣的知识.
支持区间修改区间查询
class RURQFenwickTree:
def __init__(self, size):
self.ft1 = FenwickTree(size)
self.ft2 = FenwickTree(size)
def _update(self, index, delta):
self.ft1.update(index, delta)
self.ft2.update(index, delta * index)
def update(self, index, delta):
self.update_range(index, index, delta)
def update_range(self, left, right, delta):
self._update(left, delta)
self._update(right + 1, -delta)
def query_prefix(self, index):
return (index + 1) * self.ft1.query_prefix(index) - self.ft2.query_prefix(index)
def query_range(self, left, right):
return self.query_prefix(right) - self.query_prefix(left - 1)
def query(self, index):
return self.query_range(index, index)
例题
证明
差分数组 和原数组 的关系
在代码中, 是原数组, 而 是它的差分数组, 定义为:
这样,原数组 可以表示为差分数组 的前缀和:
区间和 sum(a[l:r]) 的推导
现在考虑计算区间 [l, r] 的和:
这个双重求和可以重新排列:
对于以上操作, 更直观的理解, 考虑由组成的”三角形”, 由上倒下分别:
可以发现, 期间有个, 个直到一个. 所以式子可以如此转化:
当只需要时, 相当于大三角形减去小三角形:
可以得到:
使用树状数组优化计算
为了高效计算这个和, 代码使用了两个树状数组:
tr1[i]: 维护差分数组 的前缀和(即sum(b[1:i]))tr2[i]: 维护i * b[i]的前缀和(即sum(j * b[j] for j in range(1,i+1)) 这样, 区间和可以表示为:
其中 query(p) 计算的是:
即:
到此, 我们完成了区间查询的证明.