range queries on a static array, for idempotent operations. preprocessing and memory.
The structure
= the aggregate of the range .
const int LOG = 20;
int sp[LOG][MAXN], lg[MAXN];
void build(vector<int>& a) {
int n = a.size();
lg[1] = 0;
for (int i = 2; i <= n; i++) lg[i] = lg[i/2] + 1;
for (int i = 0; i < n; i++) sp[0][i] = a[i];
for (int k = 1; k < LOG; k++)
for (int i = 0; i + (1 << k) <= n; i++)
sp[k][i] = min(sp[k-1][i], sp[k-1][i + (1 << (k-1))]);
}
int query(int l, int r) { // inclusive [l, r]
int k = lg[r - l + 1];
return min(sp[k][l], sp[k][r - (1 << k) + 1]);
}The query covers with two overlapping blocks of length . Overlap is why the operation must be idempotent.
Idempotence — which operations work
| Operation | ? | Sparse table? |
|---|---|---|
| , | yes | ✔ |
| , | yes | ✔ (with a for the gcd itself) |
| bitwise AND, OR | yes | ✔ |
| , , XOR | no | ✘ — double-counts the overlap |
For non-idempotent operations use a disjoint sparse table ( query, non-overlapping blocks) or a segment tree.
Choosing a range-query structure
| Updates | Operation | Structure | Query |
|---|---|---|---|
| none | idempotent | sparse table | |
| none | any associative | disjoint sparse table | |
| none | sum | prefix sum | |
| point | invertible | Fenwick | |
| point/range | any associative | segment tree | |
| — | RMQ, build | Farach-Colton-Bender |
Sparse tables are the right default whenever the array never changes: the constant factor is tiny and the code is fifteen lines.
Uses
- LCA via Euler tour + RMQ on depths — the fastest practical LCA
- LCP of arbitrary suffixes — RMQ over the LCP array
- Range GCD queries
- Binary lifting is structurally the same doubling table, applied to a tree instead of an array
- Static range min for two-pointer / divide-and-conquer algorithms
2D sparse table
over rectangles of size . Build , query with four overlapping rectangles. Memory is the problem — a grid needs entries. Usually a sqrt decomposition or per-row sparse tables are more practical.
Memory
ints. For and that is 80 MB — often too much. Fixes: reduce LOG to , use int not long long, or switch to a segment tree ( memory, query).
See also: Disjoint Sparse Table · Segment Tree · Range Minimum Query