Range minimum query (RMQ) problems show up in coding interviews and contests: given a fixed array, answer many "minimum on [l, r]" questions. If the array never changes, a sparse table answers each query in constant time.
st[j][i] stores the minimum of the window starting at i with length 2^j. Row 0 is the array itself; each next row combines two halves of the previous one.
def build(a):
n = len(a); LOG = n.bit_length()
st = [a[:]]
for j in range(1, LOG):
prev, half = st[-1], 1 << (j - 1)
st.append([min(prev[i], prev[i + half])
for i in range(n - (1 << j) + 1)])
return st
def query(st, l, r): # inclusive
j = (r - l + 1).bit_length() - 1
return min(st[j][l], st[j][r - (1 << j) + 1])
Pick the largest power of two that fits in the range, then cover [l, r] with two windows of that length: one starting at l, one ending at r. They overlap, but that's fine because min is idempotent: counting an element twice doesn't change the answer.
That overlap trick works for min, max, gcd and bitwise and/or. It does not work for sums or products. For those, you'd decompose the range into O(log n) non-overlapping power-of-two blocks, at which point a prefix sum or Fenwick tree is simpler.
Say this explicitly: "The array is static and there are many queries, so I'll use a sparse table; if updates were allowed I'd switch to a segment tree."
Disclosure: written by the team behind linkjob.ai, a real-time interview assistant. Use these notes freely for practice.