0
点赞
收藏
分享

微信扫一扫

LCA(最近公共祖先)

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

 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'&&ch<='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 条评论