最近公共祖先顾名思义,就是在一棵树上,距离的两个点距离最近的公共祖先
倍增LCA算法
倍增LCA是一个在线算法,基本步骤:
1.存图
2.我们需要预处理出deep[ x ](表示点的深度),f[ x ][ i ](表示x向上跳步的祖先)
3.然后在询问x,y两点间的最近公共祖先时:
(1)将层数较深的点用f数组向上跳,通过二进制拆分思想,找到层数较深的点与层数较浅的点层数相同的祖先,使得两点跳到 同一层。
(2)若当前x=y,说明已经找到了LCA,LCA=x or y
若当前x!=y,就继续运用二进制拆分思想,让两点一起向上跳,保证两者深度一致但不相会
此时x,y一定只差一步就相会,因此LCA就等于当前x,y的父节点
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int fir[1000005],nxt[1000005],tto[1000005];
int deep[500005];//树的深度
int f[500005][25];
int p=0;
void add(int x,int y)
{
p++;
nxt[p]=fir[x];
fir[x]=p;
tto[p]=y;
}
void dfs(int k)
{
for(int i=fir[k];i!=-1;i=nxt[i])
{
if( deep[tto[i]]==-1 )//当没有遍历过这一点时
{
f[ tto[i] ][0]=k;
deep[ tto[i] ]=deep[k]+1;
dfs(tto[i]);
}
}
}
int lca(int x,int y)
{
if(deep[x]<deep[y]) swap(x,y);//如果y深度大于x,就交换x,y,保证x深度较大
for(int i=22;i>=0;i--)//通过二进制拆分将x向上跳,使x,y的深度相同
{
if(deep[ f[x][i] ]>=deep[y] )
{
x=f[x][i];
}
}//当前x,y的深度相同
if(x==y) return y;//若当前x=y,说明已经找到了LCA,LCA=x or y
for(int i=22;i>=0;i--)//通过二进制拆分将x,y同时向上跳
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];//此时x,y一定只差一步就相会,因此LCA就等于当前x,y的父节点
}
int main()
{
memset(fir,-1,sizeof(fir));//初始化
memset(nxt,-1,sizeof(nxt));
memset(deep,-1,sizeof(deep));
int n,m,s; scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=n-1;i++) //输入+存图
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
deep[s]=1; f[s][0]=0;
dfs(s); //初始化deep
for(int j=1;j<=22;j++)//初始化f,ST算法(DP)
for(int i=1;i<=n;i++)
{
f[i][j]=f[ f[i][j-1] ][j-1];
}
for(int i=1;i<=m;i++)
{
int aq,bq; scanf("%d%d",&aq,&bq);
int ans=lca(aq,bq);
printf("%d\n",ans);
}
return 0;
}