Appearance
简介
最近公共祖先(Least Common Ancestors,简称 LCA)。
节点
如何求两个节点的 LCA?
暴力算法( | LCA 算法( | |
---|---|---|
预处理 | ||
单次查询 | ||
cpp
int d[]; // d[u]: 节点 u 的深度
int f[]; // f[u]: 节点 u 的父节点
int LCA(int p, int q) {
if (d[p] < d[q]) swap(p, q); // 使 p 的深度 ≥ q
while (d[p] > d[q]) p = f[p]; // 步骤 1
while (p != q) p = f[p], q = f[q]; // 步骤 2
return p;
}
原理
暴力算法慢在哪里?
--
进一步思考,开一个二维数组
一张图中最多有
个节点,否则光是读入这张图都会超时。因此数组只要开到 。
第一步
如果已经预处理出了
采用「试跳法」。枚举
- 若
的深度小于 的深度,会跳过头,所以选择不跳; - 否则就令
。
为什么要从
时间复杂度为
cpp
for (int i = 24; i >= 0; i --)
if (d[f[p][i]] < d[q]) continue;
else p = f[p][i];
第二步
令
仍然使用「试跳法」,枚举
- 若
,此时 同时向上跳 步会相遇,但是我们选择不跳; - 若
,令 ,此时 离相遇又近了一步。
枚举结束时,
cpp
for (int i = 24; i >= 0; i --) {
if (f[p][i] != f[q][i]) {
p = f[p][i];
q = f[q][i];
}
}
合并两个步骤,得到求 LCA 的代码:
cpp
const int LogN = 24;
int LCA(int x, int y) {
if (d[x] < d[y])
swap(x, y);
for (int i = LogN; i >= 0; i --) {
if (d[f[x][i]] >= d[y]) x = f[x][i];
if (x == y) return x; // 如果已经相遇,x 和 y 就是 LCA
}
for (int i = LogN; i >= 0; i --) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0]; // 返回的是父节点
}
预处理
出于 LCA()
函数的需要,我们还需要预处理:
- 每个节点的深度
; 数组。
首先
计算顺序:
如果你还不理解为什么要终止于
采用 树形 DP 方法进行预处理。时间复杂度为
cpp
const int N = 1e6, LogN = 24;
int n, d[N + 1], f[N + 1][LogN + 1];
vector<int> g[N + 1]; // g[u]: 与节点 u 相连的点集合
void pre(int u, int fa) { // u 的父节点是 fa
f[u][0] = fa;
d[u] = d[fa] + 1;
for (int i = 0; i < LogN; i ++)
f[u][i + 1] = f[f[u][i]][i];
for (int i = 0; i < g[u].size(); i ++) {
int v = g[u][i];
if (v == fa) continue;
pre(v, u);
}
}
int main() {
/* ...省略数据的输入... */
pre(root, 0); // root 为根节点,如果题目没指定,可以为任意节点
}
模板
cpp
const int N = 1e6, LogN = 24;
int n, d[N + 1], f[N + 1][LogN + 1];
vector<int> g[N + 1];
void pre(int u, int fa) {
f[u][0] = fa;
d[u] = d[fa] + 1;
for (int i = 0; i < LogN; i ++)
f[u][i + 1] = f[f[u][i]][i];
for (int i = 0; i < g[u].size(); i ++) {
int v = g[u][i];
if (v == fa) continue;
pre(v, u);
}
}
int LCA(int x, int y) {
if (d[x] < d[y])
swap(x, y);
for (int i = LogN; i >= 0; i --) {
if (d[f[x][i]] >= d[y]) x = f[x][i];
if (x == y) return x;
}
for (int i = LogN; i >= 0; i --) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}