关于洛谷 P3379 【模板】最近公共祖先(LCA)

本文详细介绍了在有根树中使用倍增法求解最近公共祖先(LCA)的算法实现,通过具体示例和代码解析,帮助读者理解并掌握LCA算法的核心思想。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考原文
(建议你顺手点个赞👍)
首先来介绍一下什么是LCA:

LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。 ———来自百度百科
额~~看不懂没关系,我来举个栗子🌰,你和小A是表兄弟,LCA就是求你和小A的哪一个长辈是同一个人;
并且这个人辈分越小越好。

再来举个栗子🌰:
在这里插入图片描述
11和12的LCA为2,4和9的LCA为4,10和6的LCA为2
在这里我讲一下倍增法
就是就当前节点的父亲(我们称为0倍祖宗),爷爷(1倍祖宗),爷爷的父亲(2倍祖宗),爷爷的爷爷~~;
以此类推;

void cl()//par[x][y]表示y的x倍祖宗
{
	for(int k=0;k+1<MAX_LOG_N;k++)
	{
		for(int v=1;v<=a;v++)
		{
			if(par[k][v]<0)
			par[k+1][v]=-1;//在这里我们提前设根节点的父亲为-1;
			else 
			par[k+1][v]=par[k][par[k][v]];
		}
	}
}

接下来就可以开始你的操作了
(敬告读者:快读快写不要少,return 0必需有)

#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
		f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
inline void out(int a){
   if(a<0) putchar('-'),a/=-1;
   if(a>9) out(a/10);
   putchar(a%10+'0');
}
int a,b,c,d,e,shen[500005],par[20][500005],q1,q2, MAX_LOG_N;
vector<int> q[500005];
void dfs(int v,int p,int d)
{
	par[0][v]=p;
	shen[v]=d;
	for(int i=0;i<q[v].size();i++)
	{
		if(q[v][i]!=p) dfs(q[v][i],v,d+1);
	}
}
void cl()
{
	for(int k=0;k+1<MAX_LOG_N;k++)
	{
		for(int v=1;v<=a;v++)
		{
			if(par[k][v]<0)
			par[k+1][v]=-1;
			else 
			par[k+1][v]=par[k][par[k][v]];
		}
	}
}
int lca(int u,int v)
{
	if(shen[u]>shen[v])
	swap(u,v);
	for(int k=0;k<MAX_LOG_N;k++)
	{
		if(shen[v]-shen[u]>>k&1)
		{
			v=par[k][v];
		}
	}
	if(u==v)
	return u;
	for(int k=MAX_LOG_N-1;k>=0;k--)
	{
		if(par[k][v]!=par[k][u])
		{
			u=par[k][u];
			v=par[k][v];
		}
	}
	return par[0][u];
}
int main()
{
	a=read();b=read();c=read();
    MAX_LOG_N=log(a)/log(2);
	for(int i=1;i<a;i++)
	{
		d=read();e=read();
		q[d].push_back(e);
		q[e].push_back(d);
	}
    dfs(c,-1,0);
    cl();
    for(int i=1;i<=b;i++)
	{
		q1=read();q2=read();
		out(lca(q1,q2));
		putchar('\n');
	}
return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值