78 lines
3.4 KiB
Org Mode
78 lines
3.4 KiB
Org Mode
#+title: Bit Tree
|
|
* binary indexd tree
|
|
#+begin_src python :results output
|
|
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)
|
|
#+end_src
|
|
- 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.
|
|
#+begin_src python
|
|
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
|
|
#+end_src
|
|
- 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?
|
|
:PROPERTIES:
|
|
:ANKI_NOTE_TYPE: Basic
|
|
:END:
|
|
** Front
|
|
Why doesn't min work well with Fenwick/Bit trees? What property does addition have that min lacks?
|
|
** Back
|
|
#+begin_quote
|
|
Fenwick trees require an =invertible= operation (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 =update= breaks 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.
|
|
#+end_quote
|