知乎上的板子
倍增LCA
我们用一个数组
f
a
[
i
]
[
k
]
fa[i][k]
fa[i][k] 存储
i
i
i 号点的
2
k
2^k
2k 级祖先。(父节点为
1
1
1 级祖先,祖父结点为
2
2
2 级祖先……以此类推)
f
a
[
c
u
r
]
[
i
]
=
f
a
[
f
a
[
c
u
r
]
[
i
−
1
]
]
[
i
−
1
]
fa[cur][i] = fa[fa[cur][i - 1]][i - 1]
fa[cur][i]=fa[fa[cur][i−1]][i−1]
这个意思应该是
c
u
r
cur
cur 节点的
2
i
−
1
2^{i-1}
2i−1 级祖先的
2
i
−
1
2^{i-1}
2i−1 级祖先,也就是
c
u
r
cur
cur 的
2
i
2^i
2i 级祖先
树上两点
u
,
v
u,v
u,v 的距离,有公式
d
i
s
u
,
v
=
d
e
p
u
+
d
e
p
v
−
2
d
e
p
l
c
a
(
u
,
v
)
dis_{u,v}=dep_{u}+dep_{v}-2dep_{lca(u,v)}
disu,v=depu+depv−2deplca(u,v)
O
(
n
l
o
g
n
)
预
处
理
,
O
(
l
o
g
n
)
查
询
,
空
间
复
杂
度
O
(
n
l
o
g
n
)
O(nlogn)预处理,O(logn)查询,空间复杂度O(nlogn)
O(nlogn)预处理,O(logn)查询,空间复杂度O(nlogn)
当然,以上都是针对无权树的,如果有权值,可以额外记录一下每个点到根的距离,然后用几乎相同的公式求出。
code:
int Log2[MAXN], fa[MAXN][20], dep[MAXN]; // fa的第二维大小不应小于log2(MAXN)
bool vis[MAXN];
void dfs(int cur, int fath = 0)
{
if (vis[cur])
return;
vis[cur] = true;
dep[cur] = dep[fath] + 1;
fa[cur][0] = fath;
for (int i = 1; i <= Log2[dep[cur]]; ++i)
fa[cur][i] = fa[fa[cur][i - 1]][i - 1];
for (int eg = head[cur]; eg; eg = edges[eg].next)
dfs(edges[eg].to, cur);
}
int lca(int a, int b)
{
if (dep[a] > dep[b])// 不妨设a的深度小于等于b
swap(a, b);
while (dep[a] != dep[b])// 跳到深度相等为止
b = fa[b][Log2[dep[b] - dep[a]]];// b不断往上跳
if (a == b)
return a;
for (int k = Log2[dep[a]]; k >= 0; k--)
if (fa[a][k] != fa[b][k])
a = fa[a][k], b = fa[b][k];
return fa[a][0];
}
int main()
{
// ...
for (int i = 2; i <= n; ++i)// 预处理 log2(i)
Log2[i] = Log2[i >> 1] + 1;
// ...
dfs(s); // 无根树可以随意选一点为根
// ...
return 0;
}