0
点赞
收藏
分享

微信扫一扫

LCA(最近公共祖先)

云竹文斋 2022-02-27 阅读 80

 P3379 【模板】最近公共祖先(LCA)

#include<bits/stdc++.h>
using namespace std;
struct Edge
{
int to,next;
}edge[500005*2];//无向图,两倍开
int head[500005],grand[500005][21],depth[500005],lg[500001];
int cnt,n,m,s;

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''9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}

void add(int x,int y)
{
edge[++cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt;
}

void dfs(int now,int fa)
{
depth[now]=depth[fa]+1;
grand[now][0]=fa;
for(int i=1;i<=lg[depth[now]];i++)
//for(int i=1;(1<<i)<=depth[now];i++)
grand[now][i]=grand[grand[now][i-1]][i-1];
//爸爸的爸爸叫爷爷~~~
for(int i=head[now];i;i=edge[i].next)
//遍历和当前结点相连的所有的边(按输入的倒序),最后一条边的 edge[i].next==0
{
cout<<"第"<<i<<"条边,指向" <<edge[i].to<<endl;
if(edge[i].to!=fa)
dfs(edge[i].to,now);
}
}

int LCA(int a,int b)
{
if(depth[a]<depth[b])
swap(a,b);
while(depth[a]>depth[b])
a=grand[a][lg[depth[a]-depth[b]]-1];
//倍增法逼近,e.g:depth[a]-depth[b]==14
//lg[depth[a]-depth[b]]-1==3,a上升8个深度,depth[a]-depth[b]==6;
//lg[depth[a]-depth[b]]-1==2,a上升4个深度,depth[a]-depth[b]==2;
//lg[depth[a]-depth[b]]-1==1,a上升2个深度,depth[a]-depth[b]==0;
if(a==b) return a;//a和b的LCA就是a
for(int k=lg[depth[a]]-1;k>=0;k--)
if(grand[a][k]!=grand[b][k])
a=grand[a][k],b=grand[b][k];
//从远古祖先(注意不要越界)中逐渐向最近的试探
// e.g:depth[a]==14,depth[LCA]==7;
// k=lg[depth[a]]-1,k==3;grand[a][k]==grand[b][k];continue;
//k==2,grand[a][k]!=grand[b][k],a,b一起向上4个深度;
//k==1,grand[a][k]!=grand[b][k],a,b一起向上2个深度;
//k==0,grand[a][k]!=grand[b][k],a,b一起向上1个深度;
//一共向上4+2+1==7个深度,找到LCA
return grand[a][0];
}

int main()
{
n=read(),m=read(),s=read();
for(int i=1;i<n;i++)
{
int a,b;
a=read(),b=read();
add(a,b);
add(b,a);
}
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+((1<<lg[i-1])==i);//log_{2}{i}+1
dfs(s,0);//从根结点开始搜索
while(m--)
{
int x,y;
x=read(),y=read();
printf("%d\n",LCA(x,y));
}
return 0;
}

 

 

举报

相关推荐

0 条评论