"""Sparse table for O(1) range queries over an idempotent associative op. Idempotent means f(x, x) == x (min, max, gcd, bitwise AND/OR). For such ops the overlap trick gives O(1) queries after O(n log n) preprocessing. Usage: st = SparseTable([3,1,4,1,5,9,2,6]) st.query(1, 4) # min of positions 1..4 (inclusive, 0-indexed) -> 1 """ from __future__ import annotations import math from typing import Callable, Sequence class SparseTable: def __init__(self, data: Sequence, func: Callable = min): self.n = len(data) self.func = func self._log = [0] * (self.n + 1) for i in range(2, self.n + 1): self._log[i] = self._log[i // 2] + 1 self.k = self._log[self.n] if self.n > 0 else 0 # st[j][i] = func over data[i .. i + 2^j - 1] self.st = [] if self.n > 0: self.st.append(list(data)) j = 1 while (1 << j) <= self.n: prev = self.st[j - 1] span = 1 << (j - 1) cur = [func(prev[i], prev[i + span]) for i in range(self.n - (1 << j) + 1)] self.st.append(cur) j += 1 def query(self, l: int, r: int): """Inclusive range [l, r], 0-indexed. Raises on empty/invalid.""" if self.n == 0: raise IndexError("empty table") if l < 0 or r >= self.n or l > r: raise IndexError(f"invalid range [{l}, {r}] for n={self.n}") j = self._log[r - l + 1] return self.func(self.st[j][l], self.st[j][r - (1 << j) + 1]) def __len__(self): return self.n