最近公共祖先(LCA)

LCA

定义 性质:

在有根树上,LCA 是指一个点集的公共祖先中深度最大的点。

  • LCA 具有可并性。

  • LCA(u,v)u,v 间的路径上。

  • h(i) 表示 i 与根的距离,则两点 u,v 的距离 =h(u)+h(v)2h(LCA(u,v))

求法:

倍增。

fau,i 表示 u 的第 2i 级祖先,在求 LCA(u,v) 时,先将 u,v 跳转到同一深度,再枚举 k,尝试将 u,v 跳到自己的 k 级祖先,并保证不会多跳到它们的公共祖先上,即保证它们不同,如果它们不相等,则跳,否则不跳。

LCA

u

...

u跳到这里

Node1

v跳到这里

v

Node2

到最后时,u,v 恰好是 LCA 的孩子,fau,0 即为 u,vLCA

预处理 fa 的方法:DFSfau,0 为自己的父亲,因为 2i1+2i1=2i,所以 u 的第 2i1 级祖先的第 2i1 级祖先就是 u 的第 2i1+2i1=2i 级祖先,所以转移方程为:

fau,i=fafau,i1,i1

2^i-1

2^i-1

2^i-1 + 2^i-1 = 2^i

u

v

w

时间复杂度:设询问 m 次,Θ(nlogn+mlogn)

代码:

#include<iostream>
#include<vector>
#define int long long
using namespace std;
const int N = 500010;
int lg[N];
int n, m, rt;
vector<int> G[N];
int de[N], fa[N][25];
void dfs(int fat, int u) // 预处理
{
fa[u][0] = fat;
de[u] = de[fat] + 1; // 初始化
for(int i=1; i<=lg[de[u]]; i++)
fa[u][i] = fa[fa[u][i-1]][i-1]; // 转移
for(int i=0; i<G[u].size(); i++)
{
int v = G[u][i];
if(v == fat)
continue;
dfs(u, v); // 搜索
}
}
int LCA(int x, int y)
{
if(de[x] < de[y]) // 保证y深于x,便于计算
swap(x, y);
while(de[x] > de[y])
x = fa[x][lg[de[x] - de[y]] - 1]; // 统一深度
if(x == y)
return x; // 特判
for(int k=lg[de[x]]-1; k>=0; k--)
if(fa[x][k] != fa[y][k])
x = fa[x][k],
y = fa[y][k]; // 跳转到LCA的下方
return fa[x][0];
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
for(int i=1; i<N; i++)
lg[i] = lg[i-1] + ((1 << lg[i-1]) == i);
cin >> n >> m >> rt;
for(int i=1; i<n; i++)
{
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(0, rt);
while(m --)
{
int x, y;
cin >> x >> y;
cout << LCA(x, y) << '\n';
}
return 0;
}

本文作者:gctiruct

本文链接:https://www.cnblogs.com/gctiruct/p/18100841

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   gctiruct  阅读(42)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑