Translation Notice
This article was machine-translated using DeepSeek-R1.
- Original Version: Authored in Chinese by myself
- Accuracy Advisory: Potential discrepancies may exist between translations
- Precedence: The Chinese text shall prevail in case of ambiguity
- Feedback: Technical suggestions regarding translation quality are welcomed
Preface
The Lowest Common Ancestor, abbreviated as LCA, is the deepest node that is a common ancestor of two given nodes in a tree.
This algorithm has wide applications, such as efficiently solving the shortest path problem on trees.
For convenience, we denote the LCA of a node set $S=\{v_1,v_2,\ldots,v_n\}$ as $\text{LCA}(v_1,v_2,\ldots,v_n)$ or $\text{LCA}(S)$.
Partial content references OI Wiki. All algorithms in this article are implemented in C++.
Example Problem: Luogu P3379 【Template】Lowest Common Ancestor (LCA)
Properties
- $\text{LCA}(\{u\})=u$;
- $u$ is an ancestor of $v$ if and only if $\text{LCA}(u,v)=u$;
- If neither $u$ is an ancestor of $v$ nor $v$ is an ancestor of $u$, then $u$ and $v$ reside in two different subtrees of $\text{LCA}(u,v)$;
- In pre-order traversal, $\text{LCA}(S)$ appears before all elements in $S$, while in post-order traversal, it appears after all elements in $S$;
- The LCA of the union of two node sets is the LCA of their individual LCAs: $\text{LCA}(A\cup B)=\text{LCA}(\text{LCA}(A), \text{LCA}(B))$;
- The LCA of two nodes always lies on the shortest path between them;
- $d(u,v)=h(u)+h(v)-2h(\text{LCA}(u,v))$, where $d$ is the distance between two nodes on the tree, and $h$ represents the distance from a node to the root.
Solving Algorithms
Prerequisite 1: Adjacency List Storage of Trees
In simple terms, adjacency list storage means for each node, store all nodes directly reachable via one directed or undirected edge.
Traditional implementations use linked lists (or simulated linked lists), which can be cumbersome and error-prone. For better readability, we use vector
from the STL.
|
|
To add an undirected edge $u\leftrightarrow v$:
|
|
To add a directed edge $u\to v$:
|
|
Traverse all nodes directly reachable from $v$:
|
|
Prerequisite 2: DFS Traversal & Node Depth Calculation
Both algorithms require preprocessing node depths.
The depth of a node is defined as its distance from the root.
Preprocessing all node depths is straightforward using a tree DP approach. Let $h_u$ denote the depth of node $u$:
|
|
Naive Algorithm
Let $u,v$ be the nodes whose LCA is sought. Preprocess each node’s parent (denoted as $f_v$).
Steps:
- Equalize depths of $u$ and $v$: Move the deeper node upward until both depths match.
- While $u \ne v$: Set $u \gets f_u$, $v \gets f_v$.
- When $u = v$, this value is the LCA.
Time Complexity:
- Preprocessing: $\mathcal O(N)$ via DFS.
- Per Query: Worst-case $\mathcal O(N)$, average $\mathcal O(\log N)$ (for random trees with height $\lceil\log N\rceil$).
Reference Code:
|
|
This code results in TLE on the last four test cases due to worst-case $\mathcal O(NQ)$ complexity when $N,Q\le 5\times 10^5$.
Doubling Algorithm
The doubling algorithm improves the naive approach and is the classic LCA solution.
Preprocessing:
- Let $\text{fa}_{x,i}$ denote the $2^i$-th ancestor of node $x$.
- During DFS, preprocess $\text{fa}_{x,i}$:
- For $i=0$, $\text{fa}_{x,0}$ is the parent of $x$.
- For $i>0$, $\text{fa}_{x,i} = \text{fa}_{\text{fa}_{x,i-1}, i-1}$.
Query Steps:
- Equalize depths using binary lifting.
- If $u = v$ after equalizing, return $u$.
- Simultaneously move $u$ and $v$ upward until their ancestors converge. The LCA is $\text{fa}_{u,0}$.
Time Complexity:
- Preprocessing: $\mathcal O(N \log N)$.
- Per Query: $\mathcal O(\log N)$.
Reference Code:
|
|
Exercises
- Problem: Luogu P8805 [Blue Bridge Cup 2022 National B] Computer Room
- Solution: https://best-blogs.blog.luogu.org/solution-p8805
Summary
This article details LCA problem-solving using two algorithms. Comparison:
Algorithm | Preprocessing Time | Per Query Time1 | Space | Passes Example2? |
---|---|---|---|---|
Naive | $\mathcal O(N)$ | $\mathcal O(N)$ | $\mathcal O(N)$ | ❌ |
Doubling | $\mathcal O(N \log N)$ | $\mathcal O(\log N)$ | $\mathcal O(N \log N)$ | ✔️ |
Your support through likes and shares is appreciated!
-
Worst-case time complexity. ↩︎
-
Example: Luogu P3379 【Template】Lowest Common Ancestor (LCA) ↩︎