给出一棵树,求各个节点的最近公共祖先。
我们先用链式前向星将树存起来,大体思路就是靠倍增向上找,当两个节点找到的祖先相等时要不超了要不就是最近公共祖先。
因此我们得先预处理出每个节点的2^n的祖先,此处的核心是,某节点的2^i祖先是其2^(i-1)祖先的2^(i-1)祖先,又每个节点必只有一个父亲,所以和该节点有关的并且非父亲的节点都是子节点继续处理即可
void dfs(int now,int dad)
{
f[now][0]=dad,depth[now]=depth[dad]+1;
for(int i=1;i<=(int)log2(depth[now]);i++) f[now][i]=f[f[now][i-1]][i-1];
for(int i=head[now];i;i=edge[i].nex) if(edge[i].tar!=dad) dfs(edge[i].tar,now);
}
LCA
对于想要达到我们预期的效果,我要先将两节点上升到同一高度,深度大的需先向上找,同时注意深度的大小关系
在同一深度是两者相等,那么祖先就是该值,直接返回即可
接着就是倍增(减)的操作了,最大向上的倍数不会高过深度的log2,当两者祖先还不相等时就是未找到,同时我们只找到最近祖先的下一层所以记住返回最后节点的祖先
int lca(int x,int y)
{
if(depth[x]<depth[y]) swap(x,y);
while(depth[x]>depth[y]) x=f[x][(int)log2(depth[x]-depth[y])];
if(x==y) return y;
for(int i=(int)log2(depth[x]-1);i>=0;i--)
{
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
}
return f[x][0];
}
完整代码
#include <bits/stdc++.h>
using namespace std;
typedef struct node
{
int tar,nex;
}node;
node edge[1000005];
int head[500005];
int tail=0;
int f[500005][25];
int depth[500005];
int logs[500005];
void add(int x,int y)
{
edge[++tail].nex=head[x];
edge[tail].tar=y;
head[x]=tail;
}
void dfs(int now,int dad)
{
f[now][0]=dad,depth[now]=depth[dad]+1;
for(int i=1;i<=(int)log2(depth[now]);i++) f[now][i]=f[f[now][i-1]][i-1];
for(int i=head[now];i;i=edge[i].nex) if(edge[i].tar!=dad) dfs(edge[i].tar,now);
}
int lca(int x,int y)
{
if(depth[x]<depth[y]) swap(x,y);
while(depth[x]>depth[y]) x=f[x][(int)log2(depth[x]-depth[y])];
if(x==y) return y;
for(int i=(int)log2(depth[x]-1);i>=0;i--)
{
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int main()
{
int n,m,s;
scanf("%d%d%d",&n,&m,&s);
for(int i=0;i<n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs(s,0);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}
return 0;
}