lca模板

题目描述

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

输入输出格式

输入格式:

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

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

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

输出格式:

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


lca是用来求最近公共祖先的算法,具体思路是先将两个点跳到等同的深度,若两点已经相等则这个点就是最近公共祖先;若不等,则两个点一起用倍增的方法

向上跳,直到两点差异不相等,再跳最后一步就是lca。

#include<iostream>

#include<cstdio>
#include<cmath>
using namespace std;
int n,m,s,ta;
int p[500001][30],deep[500001],exist[500001];//p[ i ][ j ]表示i点向上跳2的j次方后的点,deep为深度。
struct re
{
int next,t,head;
}e[500001*4];
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 * 10 + ch - '0'; ch = getchar();}
return x * f;
} //读入优化。
void build(int x,int y)
{
++ta;
e[ta].next=e[x].head;
e[x].head=ta;
e[ta].t=y;
}
void dfs(int now)
{
for(int i=e[now].head;i;i=e[i].next)
{
int go=e[i].t;
if(exist[go]!=1)
{
exist[go]=1;
if(deep[go]==0)
   {
    deep[go]=deep[now]+1;
    p[go][0]=now;
    dfs(go);
   }
}
}
}//lca建图
void init()
{
for(int i=1;(1<<i)<=n;++i)
{
for(int j=1;j<=n;++j)
{
if(p[j][i-1]!=0)
{
p[j][i]=p[p[j][i-1]][i-1];//递推出所有的p[ ] [ ],数学问题。
}
}
}
}
int lca(int a,int b)
{
int i=0;//i要用多次,所以定义在外面。
if(deep[a]<deep[b])//保证A是深的点。
swap(a,b);
for( i=0;(1<<i)<=deep[a];++i);
--i;
for(int j=i;j>=0;--j)
{
if(deep[a]-(1<<j)>=deep[b])
{
a=p[a][j];//a跳到与b相同的深度。
}
}
if(a==b)
{
return a;
}
for(int j=i;j>=0;--j)
{
if(p[a][j]!=0&&p[a][j]!=p[b][j])
{
a=p[a][j];b=p[b][j];//一起跳。
}
}
return p[a][0];//2^0等于1。
}
int main()
{
n=read();m=read();s=read();
for(int i=1;i<=n-1;++i)
{
int a,b;
a=read();b=read();
build(a,b);build(b,a);
}
exist[s]=1;
dfs(s);init();
for(int i=1;i<=m;++i)
{
int a,b;
a=read();b=read();
cout<<lca(a,b)<<'\n';
}
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值