Binary Lifting for LCA Queries

Directory

  • Preprocessing
  • LCA
  • External Function Version

Preprocessing

void solve() {
    int n, k;
    cin >> n >> k;
    vector<vector<int>> adj(n + 1);
    for (int i = 1; i <= n - 1; ++i) {
        int x, y;
        cin >> x >> y;
        adj[x].push_back(y);
        adj[y].push_back(x);
    }
    vector<vector<int>> parent(n + 1, vector<int>(22));
    vector<int> depth(n + 1);
    vector<int> leaves;

    auto dfs = [&](int cur, int pa, auto&& dfs) -> void {
        depth[cur] = depth[pa] + 1;
        if (adj[cur].size() == 1) {
            leaves.push_back(cur);
        }
        for (auto& nxt : adj[cur]) {
            if (nxt != pa) {
                parent[nxt][0] = cur;
                dfs(nxt, cur, dfs);
            }
        }
    };
    dfs(1, 0, dfs);

    // Binary lifting to compute ancestors
    for (int p = 1; p < 22; ++p) {
        for (int i = 1; i <= n; ++i) {
            parent[i][p] = parent[parent[i][p - 1]][p - 1];
        }
    }
}

LCA

auto LCA = [&](int a, int b) -> pair<int, int> {
    long long ans = 0;
    if (depth[a] < depth[b]) swap(a, b);

    // Lift a to depth of b
    while (depth[a] > depth[b]) {
        int diff = depth[a] - depth[b];
        int step = (int)log2(diff);
        a = parent[a][step];
        ans += (1LL << step);
    }

    // Lift both together until they meet
    for (int i = log2(depth[a]); i >= 0; --i) {
        if (parent[a][i] != parent[b][i]) {
            a = parent[a][i];
            b = parent[b][i];
            ans += (1LL << (i + 1));
        }
    }
    if (a != b) {
        a = parent[a][0];
        b = parent[b][0];
        ans += 2;
    }
    return {a, ans};
};

The first while loop lifts a to the same depth as b using binary decomposition.

Etxernal Function Version

inline int LCA(int a, int b) {
    if (depth[a] < depth[b]) swap(a, b);
    while (depth[a] > depth[b]) {
        int step = (int)log2(depth[a] - depth[b]);
        a = parent[a][step];
    }
    if (a == b) return a;
    for (int i = log2(depth[a]); i >= 0; --i) {
        if (parent[a][i] != parent[b][i]) {
            a = parent[a][i];
            b = parent[b][i];
        }
    }
    if (a != b) {
        a = parent[a][0];
        b = parent[b][0];
    }
    return a;
}

Usage Example with Forward Star

(For a complete example with forward star storage, see the reference: POJ 3417 Network – LCA with tree difference and forward star needing fast input)

struct Edge {
    int to;
    int next;
} edges[2 * maxn];
int head[maxn];
int total = 0;

inline void addEdge(int from, int to) {
    ++total;
    edges[total].to = to;
    edges[total].next = head[from];
    head[from] = total;
}

int parent[maxn][23];
int depth[maxn];

void dfs1(int cur, int pa) {
    depth[cur] = depth[pa] + 1;
    parent[cur][0] = pa;
    for (int p = 1; p < 22; ++p)
        parent[cur][p] = parent[parent[cur][p - 1]][p - 1];

    for (int i = head[cur]; i > 0; i = edges[i].next) {
        int nxt = edges[i].to;
        if (nxt != pa)
            dfs1(nxt, cur);
    }
}

inline int LCA(int a, int b) {
    if (depth[a] < depth[b]) swap(a, b);
    while (depth[a] > depth[b]) {
        int step = (int)log2(depth[a] - depth[b]);
        a = parent[a][step];
    }
    if (a == b) return a;
    for (int i = log2(depth[a]); i >= 0; --i) {
        if (parent[a][i] != parent[b][i]) {
            a = parent[a][i];
            b = parent[b][i];
        }
    }
    if (a != b) {
        a = parent[a][0];
        b = parent[b][0];
    }
    return a;
}

int diff[maxn];
int pass[maxn];
int cnt0, cnt1;

void dfs2(int cur, int pa) {
    pass[cur] += diff[cur];
    for (int i = head[cur]; i > 0; i = edges[i].next) {
        int nxt = edges[i].to;
        if (nxt != pa) {
            dfs2(nxt, cur);
            pass[cur] += pass[nxt];
        }
    }
}

Tags: LCA binary lifting tree algorithm

Posted on Tue, 08 Sep 2026 16:35:03 +0000 by FadeOut79