Gracekite's Homepage

Back

题目描述#

给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式#

第一行包含三个正整数 ,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来 行每行包含两个正整数 ,表示 结点和 结点之间有一条直接连接的边(数据保证可以构成树)。

接下来 行每行包含两个正整数 ,表示询问 结点和 结点的最近公共祖先。

输出格式#

输出包含 行,每行包含一个正整数,依次为每一个询问的结果。

样例 #1#

样例输入 #1#

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
plaintext

样例输出 #1#

4
4
1
4
4
plaintext

提示#

对于 的数据,,。

对于 的数据,,。

对于 的数据,,。

样例说明:

该树结构如下:

第一次询问: 的最近公共祖先,故为 。

第二次询问: 的最近公共祖先,故为 。

第三次询问: 的最近公共祖先,故为 。

第四次询问: 的最近公共祖先,故为 。

第五次询问: 的最近公共祖先,故为 。

故输出依次为 。

{% raw %}
{% endraw %}

算法分析#

本题通过倍增的方法实现

首先我们要记录各个点的深度和他们级的的祖先,用数组表示每个节点的深度,表示节点ii的级祖先。

void dfs(int now, int fath) {
	fa[now][0] = fath; depth[now] = depth[fath] + 1;
	for(int i = 1; i <= lg[depth[now]]; ++i)
		fa[now][i] = fa[fa[now][i-1]][i-1];
    for(int i = 0;i<=po[now].size()-1;i++){
        if(po[now][i]!=fath) dfs(po[now][i],now);
    }
}
cpp

加一个常数优化

for(int i = 1;i<=n;i++)lg[i] = lg[i-1] + (1 << lg[i-1] == i);//解释: gl[深度]指向2的i次方,gl[]中存的是i。
cpp

倍增LCA,先把两个点提到同一高度,再统一开始跳。

if(depth[x]<depth[y])swap(x,y);
for(;depth[x]>depth[y];){
    x = fa[x][lg[depth[x]-depth[y]] - 1];
}//提到同一高度
cpp

但我们在跳的时候不能直接跳到它们的LCA,因为这可能会误判,所以我们要跳到它们LCA的下面一层,然后输出它们的父节点,这样就不会误判了。

if(x==y)return x;
for(int k = lg[depth[x]] - 1; k >= 0; --k)
	if(fa[x][k] != fa[y][k])
		x = fa[x][k], y = fa[y][k];
return fa[x][0];
cpp

{% raw %}

{% endraw %} {% raw %}
{% endraw %}

{% raw %}

{% endraw %}

LCA最近公共祖先模板
https://betteroier.github.io/blog/lca%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88%E6%A8%A1%E6%9D%BF
Author Yuxuan Zheng
Published at August 2, 2022