如何求树上最近公共祖先LCA
如何求树上最近公共祖先LCA
求树上最近公共祖先一般常用的有三种方法
| 预处理时间复杂度 | 查询时间复杂度 | |
|---|---|---|
| 树上倍增 | \(O(nlogn)\) | \(O(logn)\) |
| 树链剖分 | \(O(n)\) | \(O(logn)\) |
| 欧拉序+ST表 | \(O(nlogn)\) | \(O(1)\) |
在一般的使用情景下,前两种做法即可满足大多数的题目要求,相比较而言,树链剖分的求法常数更小,更为优秀(也是我主要使用的方法)。在需要大量查询的题目要求下,可以使用第三种方法。
树链剖分求LCA
主要函数部分
int lca(int x, int y)
{
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]])
swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
基本原理
使用树链剖分求出top,dep等信息。从给定的两个点中,选择深度更深的一个往上跳,直到两个点的top值相等,此时两个点在同一条链上,取出深度较小的点即为LCA。在最坏情况下,给定的树是一颗满二叉树,此时时间复杂度达到\(O(logn)\)
欧拉序+ST表求LCA
主要代码
const int maxn = 1e5;
struct LCA
{
int ST[(maxn << 1) + 10][maxbit], dfsn[(maxn << 1) + 10], dep[maxn + 10];
int lg2[(maxn << 1) + 10];
int cnt;
inline void dfs(int p, int fa)
{
dfsn[p] = ++cnt;
dep[p] = dep[fa] + 1;
ST[cnt][0] = p;
for (auto q : edges[p]) {
if (q == fa)
continue;
dfs(q, p);
ST[++cnt][0] = p;
}
}
inline void init(int root)
{
cnt = 0;
dfs(root, 0);
int n = cnt;
for (int i = 2; i <= n; i++)
lg2[i] = lg2[i >> 1] + 1;
for (int j = 1; (1 << j) <= n; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
if (dep[ST[i][j - 1]] < dep[ST[i + (1 << (j - 1))][j - 1]])
ST[i][j] = ST[i][j - 1];
else
ST[i][j] = ST[i + (1 << (j - 1))][j - 1];
}
}
}
inline int query(int u, int v)
{
int l = dfsn[u];
int r = dfsn[v];
if (l > r)
swap(l, r);
int dt = r - l + 1;
int k = lg2[dt];
if (dep[ST[l][k]] <= dep[ST[r - (1 << k) + 1][k]])
return ST[l][k];
else
return ST[r - (1 << k) + 1][k];
}
};
基本原理
先进行一遍dfs求出每个点的欧拉序,通过欧拉序的性质我们可以知道,两个点之间深度最小的点即为这两点的LCA,所以我们使用ST表进行预处理,预处理时使用dep进行比较(在一般的ST表中通常直接比较权值),这样我们就可以快速\(O(1)\)求出\([l,r]\)之间深度最小的点,即为我们所求的LCA

浙公网安备 33010602011771号