LCA——最近公共祖先

最近公共祖先顾名思义,就是在一棵树上,距离的两个点距离最近的公共祖先

倍增LCA算法

倍增LCA是一个在线算法,基本步骤:

1.存图

2.我们需要预处理出deep[ x ](表示点的深度),f[ x ][ i ](表示x向上跳2^{i}步的祖先)

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_42534329/article/details/81186363