题目背景:洛谷模板题P3379 【模板】最近公共祖先(LCA)
代码实现
由于代码中的注释已经很详尽了……就不做过多解释啦(=゚ω゚)ノ
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
// MAXN 为最大节点数量
const int MAXN = 700000;
// LOG 为 `log2(MAXN)` 的近似值,
// 在算法中作为倍增时所需要的循环次数。
const int LOG = 20;
// 树节点由链表实现
struct TreeNode {
int val;
vector< TreeNode * > children;
};
// 树的基本信息
int N, M, S;
vector< int > tree[MAXN];
int parent[MAXN][LOG]; // 倍增法中的父节点
int depth[MAXN]; // 每个节点的深度
// DFS 预处理每个节点的父节点和深度
void dfs(int node, int par) {
// 将当前节点的直接父节点设置为 par
parent[node][0] = par;
// 循环计算每个节点的 2^i 个祖先
for (int i = 1; i < LOG; ++i) {
// 确保当前节点的第 2^(i-1) 个祖先存在
if (parent[node][i - 1] != -1) {
// 节点 node 的第 2^i 个祖先是:
// 其第 2^(i-1) 个祖先的第 2^(i-1) 个祖先。
parent[node][i] = parent[parent[node][i - 1]][i - 1];
} else {
// 如果当前节点的第 2^(i-1) 个祖先不存在,
// 那么 2^i 个祖先也不存在,设置为 -1。
parent[node][i] = -1;
}
}
// 遍历当前节点的每个子节点,递归调用 dfs 计算子节点的深度和倍增表。
for (int child : tree[node]) {
if (child != par) {
depth[child] = depth[node] + 1;
dfs(child, node);
}
}
}
// 利用倍增法查找两个节点的最近公共祖先
int lca(int u, int v) {
// 确保 u 是更深的节点
if (depth[u] < depth[v]) {
swap(u, v);
}
// 计算两个节点的深度差
int diff = depth[u] - depth[v];
// 将两个节点拉平到同一深度
for (int i = 0; i < LOG; ++i) {
// 使用二进制提升法将 u 提升到和 v 同一深度:
// diff >> i 检查 diff 的第 i 位是否为1
if ((diff >> i) & 1) {
// 如果为1,将 u 提升到其第 2^i 个祖先
u = parent[u][i];
}
}
// 如果拉到同深度后,节点已经相等,
// 则说明该深度对应的节点就已经是 LCA
if (u == v) {
return u;
}
// 否则,则将它们一起提升,直到相等。
for (int i = LOG - 1; i >= 0; --i) {
// 如果 u 和 v 的第 2^i 个祖先不同
// 则同时将 u 和 v 提升到它们的第 2^i 个祖先。
if (parent[u][i] != parent[v][i]) {
u = parent[u][i];
v = parent[v][i];
}
}
// 最终返回 u 和 v 的直接父节点:
// parent[u][0] 或 parent[v][0]
// 即为最近公共祖先。
return parent[u][0];
}
int main() {
// 数据输入及建树
cin >> N >> M >> S;
for (int i = 0; i < N - 1; ++i) {
int x, y;
cin >> x >> y;
// 由于我们使用邻接表来存储树结构
// 因此是无向图,故需要同时添加
// x->y 与 y->x 的连接
tree[x].push_back(y);
tree[y].push_back(x);
}
// 初始化 parent 数组
fill(parent[0], parent[0] + MAXN * LOG, -1);
// S 在题目中为根结点,其深度为0。
depth[S] = 0;
// 调用 DFS 预处理整个树,从根节点开始。
dfs(S, -1);
// 查询及返回
for (int i = 0; i < M; ++i) {
int a, b;
cin >> a >> b;
cout << lca(a, b) << endl;
}
return 0;
}