Files
cpp-flashcards/org/ds/bit_tree.org
T

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 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.