树状数组的证明
树状数组的证明
树状数组 和 线段树 是解决区间问题常用的两种数据结构。其中线段树理解起来相对简单,而树状数组的代码实现则更简洁。
参考例题: 区域和检索-数组可修改,就是一个标准的树状数组题。更抽象的应用场景可以理解为:
- 假设存在数组 ai,
- 存在操作 ⨀(ai,ai+1,ai+2,⋯,ai+k),并且
⨀(ai,ai+1,⋯,ai+k)=⨀(⨀(ai,ai+1,⋯,ai+j),⨀(ai+j+1,ai+j+2,⋯,ai+k))(1)
⨀(aj,⨀(ak))=⨀(aj,aK)(2)
- 树状数组可以 O(logn) 的时间复杂度求出 ⨀(a1,a2,a3,⋯,ai)的值。
- 这里的 ⨀ 可以是 ∑, 也可以是 ∏,或者是任意其他满足上述等式的操作。
楼主从大一就已学习树状数组,但一直断断续续的用,断断续续的忘,每次使用时都要翻出模板代码来。
最近尝试手推了一下树状数组,记录于下。
假设原数组是 ai,树状数组利用 ai 构建了新数组 bi。
bi=i−2k+1⨀iai
imod2k=0∧(∄m>k∧imod2m=0)
以 ⨀=∑ 为例,
b1=a1b2=a2+a1b3=a3b4=a4+a3+a2+a1b5=a5b6=a6+a5b7=a7b8=a8+a7+a6+a5+a4+a3+a2+a1
如下图,黑色为原数组 ai, 红色为数组 bi
假设目标为计算 ⨀i=1i=nai
利用公式 (1),可得:
i=1⨀i=nai=⨀(i=1⨀i=jai,i=j+1⨀i=nai),j=2⌊log2n⌋=⨀(bj,i=j+1⨀i=nai)=⨀(bj,⨀(i=j+1⨀i=kai,i=k+1⨀i=nai),k=j+2⌊log2(n−j)⌋=⨀(bj,⨀(bk,i=k+1⨀i=nai))=⋮=⨀(bj,⨀(bk,⨀(bl,⋯))),j=2⌊log2n⌋,k=j+2⌊log2(n−j)⌋,l=k+2⌊log2(n−k)⌋,⋯=⨀(bj,bk,bl,⋯))),j=2⌊log2n⌋,k=j+2⌊log2(n−j)⌋,l=k+2⌊log2(n−k)⌋,⋯
以 ⨀=∑ 为例,
i=1∑i=1ai=b1i=1∑i=2a2=b2i=1∑i=3a3=b3+b2i=1∑i=4a4=b4i=1∑i=5a5=b5i=1∑i=6a6=b6+b4i=1∑i=7a7=b7+b6+b4i=1∑i=8a8=b8
所以求 ⨀i=1i=nai,实际是求
⨀(bj,bk,bl,⋯))),j=2⌊log2n⌋,k=j+2⌊log2(n−j)⌋,l=k+2⌊log2(n−k)⌋,⋯
对于 i,j,k,⋯ 很容易通过数学公式计算出,但从位运算的角度理解更合适。
以 n=58 为例,二进制转换后
58=0b00111010
i=0b00100000j=0b00110000k=0b00111000l=0b00111010
容易得到,l,k,j,i,⋯从大到小相当于二进制消去最后的一个1.
二进制运算中,可以用 n−(n&−n) 逐次迭代得到 k,j,i,⋯。(其证明方式我已经找到了一个绝妙的方法,篇幅限制就不在这里展开了。)
树状数组不仅可以 O(logn) 的时间复杂度求出 ⨀(a1,a2,a3,⋯,ai)的值。当原数组ai 更新时,仍可以 O(logn) 的时间复杂度更新 bi。
假设 ai 更新,则只需要更新 bj1,bj2,bj3,⋯的值,其中
j−2k+1≤i≤j
jmod2k=0∧(∄m>k∧jmod2m=0)
同样二进制运算的方式,用 i+(i&(−i) 逐次迭代即可得到 j 序列。
下面是一个 Kotlin 实现:
class FenwickTree<T, U, R>(val size: Int, private val default: U,
val accumulateOp: (R, U) -> R,
val updateOp: (Int, T, U) -> U) {
val tree: MutableList<U> = ArrayList<U>(size).also { tree -> (0 until size).forEach { _ -> tree.add(default) } }
fun lowBit(index: Int) = index and (-index)
inline fun query(index: Int, default: R): R {
var varIndex = index
var result = default
while (varIndex > 0) {
result = accumulateOp(result, tree[varIndex])
varIndex -= lowBit(varIndex)
}
return result!!
}
inline fun update(index: Int, value: T) {
var varIndex = index
while (varIndex < size) {
tree[varIndex] = updateOp(index, value, tree[varIndex])
varIndex += lowBit(varIndex)
}
}
}