B · Grandmaster · 14 min

Binary Lifting and LCA

up[k][v] is the 2^k-th ancestor of v. After O(n log n) setup, jump any node to any ancestor in O(log n), and LCA(u,v) is two jumps to the same depth then a paired climb.

The lowest common ancestor of u and v is the deepest node that lies on both paths to the root. Naive: walk parents until you meet — O(n) per query, dead at n, q = 2·10⁵.

Binary lifting: up[0][v] = parent[v], up[k][v] = up[k-1][up[k-1][v]]. To lift v by d steps, walk the bits of d. LCA: lift the deeper node to the same depth, then lift both while their next 2^k ancestors differ.

CSES Company Queries II / Distance Queries (dist(u,v) = depth[u]+depth[v]-2·depth[lca]). USACO Gold lists this under optional Euler-tour LCA too — RMQ on the Euler tour is equivalent; lifting is the one you type first.

Rooted at 0. parent[0] = 0.js
function buildLca(n, parent, depth) {
  const LOG = Math.ceil(Math.log2(n)) + 1;
  const up = Array.from({ length: LOG }, () => Array(n).fill(0));
  up[0] = parent.slice();
  for (let k = 1; k < LOG; k++) {
    for (let v = 0; v < n; v++) up[k][v] = up[k - 1][up[k - 1][v]];
  }
  function lift(v, dist) {
    for (let k = 0; dist > 0; k++, dist >>= 1) if (dist & 1) v = up[k][v];
    return v;
  }
  function lca(u, v) {
    if (depth[u] < depth[v]) [u, v] = [v, u];
    u = lift(u, depth[u] - depth[v]);
    if (u === v) return u;
    for (let k = LOG - 1; k >= 0; k--) {
      if (up[k][u] !== up[k][v]) {
        u = up[k][u];
        v = up[k][v];
      }
    }
    return up[0][u];
  }
  return { lca, lift };
}

TRACE

Tree 0-1-3, 0-2-4. Query LCA(3,4).

Depths

depth[0]=0, [1]=[2]=1, [3]=[4]=2.

1 / 4

CHECK

parent[0] must be…

CHECK

k-th ancestor of v, or 'does not exist'. How?

Checks 0/2

Next lesson