3.4 KiB
Bit Tree
binary indexd tree
class BinaryIndexedTree:
def __init__(self, n: int):
self.tree = [0] * (n + 1)
def update(self, i: int, delta: int) -> None:
"""Add delta to element at index i (0-based)."""
idx = i + 1
while idx < len(self.tree):
self.tree[idx] += delta
idx += idx & (-idx)
def query(self, i: int) -> int:
"""Return prefix sum from 0 to i (0-based)."""
idx = i + 1
s = 0
while idx > 0:
s += self.tree[idx]
idx -= idx & (-idx)
return s
def range_query(self, l: int, r: int) -> int:
"""Return sum from l to r (0-based, inclusive)."""
if l == 0:
return self.query(r)
return self.query(r) - self.query(l - 1)
- update(i, delta) — adds delta to index i in O(log n)
- query(i) — prefix sum [0..i] in O(log n)
- range_query(l, r) — sum [l..r] in O(log n)
min bit tree
That's a min-Fenwick (prefix minimum). Unlike sum-Fenwick, updates are point-set (not add-delta), and increasing a value is expensive — you'd need a segment tree for that.
import math
class MinFenwick:
def __init__(self, n: int, inf: int = math.inf):
self.n = n
self.inf = inf
self.tree = [inf] * (n + 1)
self.arr = [inf] * n # track actual values for rebuild
def update(self, i: int, val: int) -> None:
"""Set element at index i to val (0-based)."""
self.arr[i] = val
idx = i + 1
while idx <= self.n:
# rebuild this node from its covered range
lo = idx - (idx & (-idx)) # 0-based: lo
hi = idx - 1 # 0-based: hi
self.tree[idx] = min(self.arr[j] for j in range(lo, hi + 1))
idx += idx & (-idx)
def query(self, i: int) -> int:
"""Return min from 0 to i (0-based, inclusive)."""
idx = i + 1
res = self.inf
while idx > 0:
res = min(res, self.tree[idx])
idx -= idx & (-idx)
return res
- update(i, val) — set index i to val, O(log n · span) where span is the size of the covered range
- query(i) — prefix min [0..i], O(log n)
Caveat: If you need to increase a value (removing a minimum from consideration), this rebuilds the range each time — worst case O(n). For full range-min with arbitrary increases/decreases, use a segment tree instead.
why doesn't min work with BIT?
Front
Why doesn't min work well with Fenwick/Bit trees? What property does addition have that min lacks?
Back
Fenwick trees require an
invertibleoperation (a group). Addition is invertible via subtraction: range_query(l,r) = prefix(r) - prefix(l-1). Min has no inverse — you can't "subtract out" the minimum of [0..l-1] from prefix(r) to get [l..r].Additionally, min's
updatebreaks on value increases. Decreasing a value works fine (like sum's decrease), but increasing a value means the old minimum might have been removed, and you'd need to scan all elements in the node's covered range to find the new minimum. This makes updates O(span) instead of O(log n).Good Fenwick operations are associative, have an identity element, and are incrementally updateable: sum, xor, gcd. Min/max work for prefix queries only, not range queries.