最近公共祖先
817 字
4 分钟
最近公共祖先
题目:P3379 最近公共祖先(LCA)
分析
LCA(Lowest Common Ancestor)即树上两个节点 路径上离根最近的公共节点.
常用方法有:
- 倍增法(预处理 ,单次查询 )
- 树链剖分(重链剖分)
- Tarjan 离线算法
最常用、易实现的是倍增法.
倍增法需要:
- 预处理每个节点的 级祖先(即 表示 的第 级祖先).
- 查询时先将 跳到同一深度,然后一起向上跳,距离从 到 ,如果跳到相同的节点就调回来,下一次少跳一些,直到找到最近公共祖先.
具体实现步骤:
- 建树与初始化
- 用邻接表存树.
- 记录每个节点的深度 .
- 记录每个节点的父节点 .
- 预处理倍增表
- 对每个节点 ,.
- 预处理 从 到 .
- 查询 LCA
- 若 深度不同,先将较深的节点向上跳到同一深度.
- 然后从大到小枚举 ,若 ,则 同时跳到各自的 级祖先.
- 最后 和 的父节点就是 LCA.
复杂度分析
- 预处理:
- 单次查询:
标程
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 100;
int n, m, s;vector<int> edge[N];int deep[N];int fa[N][23];
// 预处理void getInfo(int t) { // 预处理父节点 deep[t] = deep[fa[t][0]] + 1; // 预处理祖宗节点 for (int i = 1; i < 23; ++i) fa[t][i] = fa[fa[t][i - 1]][i - 1];
// 递归处理所有的边 for (int i = 0; i < edge[t].size(); ++i) { int to = edge[t][i]; if (to == fa[t][0]) continue; fa[to][0] = t; getInfo(to); }}
// 获得t节点向上跳v格跳到的节点编号int jump(int t, int v) { for (int i = 22; i >= 0; --i) { if (v >= (1 << i)) { t = fa[t][i]; v -= (1 << i); } } return t;}
// 获取节点x和节点y的lcaint lca(int x, int y) { // 统一深度 if (deep[y] > deep[x]) y = jump(y, deep[y] - deep[x]); else x = jump(x, deep[x] - deep[y]); // 处理x和y是同一支上的,有血缘关系的情况 if (x == y) return x;
// 向上跳 for (int i = 22; i >= 0; --i) { if (fa[x][i] == fa[y][i]) continue; x = fa[x][i], y = fa[y][i]; } return fa[x][0];}
int main() { cin >> n >> m >> s; for (int i = 1; i <= n - 1; ++i) { int x, y; cin >> x >> y; edge[x].push_back(y); edge[y].push_back(x); } getInfo(s); for (int i = 1; i <= m; ++i) { int x, y; cin >> x >> y; cout << lca(x, y) << endl; } return 0;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


