Problem: Lowest Common Ancestor (LCA)
Problem Description
As stated, given a rooted multi-way tree, find the lowest common ancestor of two specified nodes.
Input Format
The first line contains three positive integers $!N, M, S!$, representing the number of nodes in the tree, the number of queries, and the root node’s index, respectively.
The next $!N-1!$ lines each contain two positive integers $!x!$ and $!y!$, indicating that there is a direct connection between nodes $!x!$ and $!y!$ (it is guaranteed that the data forms a tree).
The next $!M!$ lines each contain two positive integers $!a!$ and $!b!$, representing a query for the lowest common ancestor of nodes $!a!$ and $!b!$.
Output Format
The output contains $!M!$ lines, each containing a positive integer, which is the result for each query in order.
Example #1
Input #1
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
Output #1
4
4
1
4
4
Data Range
For $!30 \% !$ of the data, $!N \leq 10!$, $!M \leq 10!$.
For $!70 %!$ of the data, $!N \leq 10000!$, $!M \leq 10000!$.
For $!100 %!$ of the data, $!1 \leq N, M \leq 500000!$, $!1 \leq x, y, a, b \leq N!$, not guaranteed $!a \neq b!$.
Code Implementation
Since the comments in the code are already very detailed… I won’t explain much further.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
// MAXN is the maximum number of nodes
const int MAXN = 700000;
// LOG is an approximation of `log2(MAXN)`,
// used as the number of iterations needed for binary lifting.
const int LOG = 20;
// Tree nodes implemented as linked lists
struct TreeNode {
int val;
vector< TreeNode * > children;
};
// Basic information of the tree
int N, M, S;
vector< int > tree[MAXN];
int parent[MAXN][LOG]; // Parent nodes for binary lifting
int depth[MAXN]; // Depth of each node
// DFS preprocesses each node's parent and depth
void dfs(int node, int par) {
// Set the direct parent of the current node to par
parent[node][0] = par;
// Loop to calculate each node's 2^i-th ancestor
for (int i = 1; i < LOG; ++i) {
// Ensure the 2^(i-1)-th ancestor of the current node exists
if (parent[node][i - 1] != -1) {
// The 2^i-th ancestor of node is:
// the 2^(i-1)-th ancestor of its 2^(i-1)-th ancestor.
parent[node][i] = parent[parent[node][i - 1]][i - 1];
} else {
// If the 2^(i-1)-th ancestor of the current node doesn't exist,
// then the 2^i-th ancestor also doesn't exist, set to -1.
parent[node][i] = -1;
}
}
// Traverse each child of the current node, recursively call dfs to
// calculate the child's depth and binary lifting table.
for (int child : tree[node]) {
if (child != par) {
depth[child] = depth[node] + 1;
dfs(child, node);
}
}
}
// Use binary lifting to find the lowest common ancestor of two nodes
int lca(int u, int v) {
// Ensure u is the deeper node
if (depth[u] < depth[v]) {
swap(u, v);
}
// Calculate the depth difference between the two nodes
int diff = depth[u] - depth[v];
// Raise both nodes to the same depth
for (int i = 0; i < LOG; ++i) {
// Use binary lifting to raise u to the same depth as v:
// diff >> i checks if the i-th bit of diff is 1
if ((diff >> i) & 1) {
// If it is 1, raise u to its 2^i-th ancestor
u = parent[u][i];
}
}
// If nodes are equal after raising to the same depth,
// the node at this depth is already the LCA
if (u == v) {
return u;
}
// Otherwise, raise both together until they are equal.
for (int i = LOG - 1; i >= 0; --i) {
// If the 2^i-th ancestors of u and v are different,
// raise both u and v to their respective 2^i-th ancestors.
if (parent[u][i] != parent[v][i]) {
u = parent[u][i];
v = parent[v][i];
}
}
// Finally, return the direct parent of u and v:
// parent[u][0] or parent[v][0]
// which is the lowest common ancestor.
return parent[u][0];
}
int main() {
// Data input and tree construction
cin >> N >> M >> S;
for (int i = 0; i < N - 1; ++i) {
int x, y;
cin >> x >> y;
// Since we use an adjacency list to store the tree structure
// it is an undirected graph, so we need to add both
// x->y and y->x connections
tree[x].push_back(y);
tree[y].push_back(x);
}
// Initialize the parent array
fill(parent[0], parent[0] + MAXN * LOG, -1);
// S is the root node in the problem, its depth is 0.
depth[S] = 0;
// Call DFS to preprocess the entire tree, starting from the root node.
dfs(S, -1);
// Query and return results
for (int i = 0; i < M; ++i) {
int a, b;
cin >> a >> b;
cout << lca(a, b) << endl;
}
return 0;
}