📅  最后修改于: 2023-12-03 15:41:56.903000             🧑  作者: Mango
在树上求LCA是树上最常见的问题,一种求LCA的常用方法是使用Tarjan算法,但是这种方法的时间复杂度为O(N+Q),其中N是树的节点数,Q是查询数。如果我们需要多次查询LCA,或者对于一颗较大的树上进行LCA查询,这种时间复杂度显然太慢了。
在这里,我们介绍一种时间复杂度为O(NlogN+Q)的LCA算法——稀疏矩阵DP方法。这种方法通过预处理出树的深度和祖先节点信息,可以在O(1)的时间内回答一个查询,因此适用于多次查询LCA的场合。
预处理出每个节点的深度$d[i]$和祖先节点$fa[i][j]$,其中$j$表示$2^j$级祖先。
void dfs(int u, int fa, int depth) {
d[u] = depth;
fa[u][0] = fa;
for (int i = 1; i <= log2(N); ++i) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (v != fa) dfs(v, u, depth + 1);
}
}
对于两个节点$x$和$y$,令$d[x]>d[y]$,先将$x$往上跳$d[x]-d[y]$步,然后起点处两点同时往上跳,直到到达它们的LCA。
int LCA(int x, int y) {
if (d[x] < d[y]) swap(x, y); // 令x为深度更深的节点
for (int i = log2(d[x] - d[y]); i >= 0; --i) {
if (d[fa[x][i]] >= d[y]) x = fa[x][i];
}
if (x == y) return x; // 特判
for (int i = log2(d[x]); i >= 0; --i) {
if (fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
时间复杂度为O(NlogN+Q)。
适用于多次查询LCA的场合,或者对于一颗较大的树上进行LCA查询。