逆天寻祖算法———倍增法求LCA(最近公共祖先)模板分享

问题引入:什么是公共祖先?

在这里插入图片描述

倍增法求LCA原理:

本质上是一个dp,类似于之前讲过的ST表,fa[i][j]表示i号节点,往上走2的j次方所到的结点,当dep[i]-pow(2,j)>=1时fa[i][j]有效(假设根节点深度为1)这个fa数组可以用dfs实现,我们看下面例子:在这里插入图片描述

由此我们可以得到fa数组的状态转移方程:

fa[x][i] = fa[fa[x][i - 1]][i - 1];

然后我们采用贪心的思想,查询LCA(x,y)时,假设X深度更深,然后从大到小枚举j,当fa[x][j]的深度不超过y的深度时,x才能往上跳,也就是说要让x往上跳,但是不能超过y,又要尽可能接近y,跳到两者的深度相等即dep[x]=dep[y],此时如果x==y,直接返回公共祖先,如果不相等,x和y同时往上跳,,最后一定会停在LCA的下方一层,我们直接返回fa[x][0]即可,即再向上跳一层,如下图:
在这里插入图片描述
我们要找6和10的LCA,令10号点为x,然后x不断向上跳,跳到7号点的位置,此时两者再往上跳就会到3就是公共祖先,所以我们返回fa[x][0]即可,即向上走一层,跳的时候只能一层一层跳否则会出现问题

倍增法求LCA的模板:

步骤1:dfs求fa数组(父亲数组):

void dfs(int x, int p)//p是父亲
{
	dep[x] = dep[p] + 1;
	fa[x][0] = p;
	for (int i = 1; i <= 20; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];//转移方程
	for (const auto& y : g[x])
	{
		if (y == p)continue;
		dfs(y, x);
	}
}

步骤2:LCA函数求公共祖先

int lca(int x, int y)
{
	if (dep[x] < dep[y])swap(x, y);//如果x深度比y小,交换x和y,使得x深度更深
	for (int i = 20; i >= 0; --i)if (dep[fa[x][i]] >= dep[y])x = fa[x][i];//贪心思想,i从大到小,x向上跳过程中,深度不能超过y
	if (x == y)return x;//跳跃过程x不能等于y
	for (int i = 20; i >= 0; --i)if (fa[x][i] != fa[y][i])x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}

题目:最近公共祖先

在这里插入图片描述
在这里插入图片描述

代码示例:

#include<iostream>
#include<vector>
using namespace std;
const int N = 1e5 + 9;
int dep[N], fa[N][24];
vector<int> g[N];
void dfs(int x, int p)
{
	dep[x] = dep[p] + 1;
	fa[x][0] = p;
	for (int i = 1; i <= 20; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for (const auto& y : g[x])
	{
		if (y == p)continue;
		dfs(y, x);
	}
}

int lca(int x, int y)
{
	if (dep[x] < dep[y])swap(x, y);
	for (int i = 20; i >= 0; --i)if (dep[fa[x][i]] >= dep[y])x = fa[x][i];
	if (x == y)return x;
	for (int i = 20; i >= 0; --i)if (fa[x][i] != fa[y][i])x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}

int main()
{
	int n; cin >> n;
	for (int i = 1; i <n; ++i)
	{
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);//读入双向边
	}
	dfs(1, 0);
	int m; cin >> m;
	while (m--)
	{
		int x, y; cin >> x >> y;
		cout << lca(x, y) << endl;
	}
	return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值