Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Fenwick Binary-Lifting Descent

โญโญโญ advancedโณ 30 min read๐Ÿ“ Lesson 130 of 281

One O(log n) walk finds the smallest position whose prefix reaches k โ€” no extra memory, just the tree's own block structure.

Finding the k-th one

A Fenwick tree stores prefix sums. Given a target k, we want the smallest index pos with prefix(pos) >= k โ€” the position of the k-th one in a 0/1 array. Scanning is O(n); the classic trick is a single top-down walk:

int pos = 0; long long rem = k;
for (int pw = LOG; pw >= 0; --pw) {
    int np = pos + (1 << pw);
    if (np <= n && fen[np] < rem) { pos = np; rem -= fen[np]; }
}
return pos + 1;   // 1-based answer

Why it works: fen[np] covers exactly the block (pos, np]. If that block's count fits inside rem, jump over it in one step; otherwise do not. After the loop, pos is the largest index whose prefix is still \< k, so the answer is pos + 1. Each level is inspected once โ€” O(log n) total. Binary-searching over prefix(i) instead would be O(log^2 n).

Guard rails:

  • k must satisfy 1 \<= k \<= prefix(n); check at the call site.
  • The same descent finds the k-th empty slot: walk on the occupied BIT and compare len - occupied (empties in the block) against rem.
  • Multiset rank queries: insert v as upd(v, 1); the k-th smallest is the descent with k.

Common bug

Forgetting rem -= fen[np] after a jump. The walk then overshoots and can even return n + 1 โ€” always test with k = 1 on an empty tree.