LCA最小公共祖先(倍增法)模板

//LCA找最近公共祖先,被增法模板 
//f[y][j]的意思是从y节点走2的j次方走到的节点 
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int d[N],dep[10],f[N][10];//d[N]记录每个节点的深度,dep记录每层节点个数 
int n,u,v,t,tot=0,ans,lca;
int head[N],Next[2*N],ver[2*N];
void add(int x,int y)
{
	ver[++tot]=y;
	Next[tot]=head[x];
	head[x]=tot;
}//领接表存边 
void bfs()
{
	queue<int> q;
	d[1]=1;
	dep[1]++;
	q.push(1);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=Next[i])
		{
			int y=ver[i];
			if(d[y]) continue;
			d[y]=d[x]+1;
			dep[d[y]]++;//存储每行有几个结点
			f[y][0]=x;//y的父节点是x 
			for(int j=1;j<=t;j++)//遍历每层 
			{
				f[y][j]=f[f[y][j-1]][j-1];
			 } 
			 q.push(y);
		}
	}
}//预处理 
int LCA(int x,int y)//s树上倍增法 
{
	if(d[x]>d[y]) swap(x,y);//保证x深度小于y
	for(int i=t;i>=0;i--)//y向上跳,跳到x同层 
	{
		if(d[f[y][i]]>=d[x])//y点还在x下面,可以跳
		{
			y=f[y][i];//更新跳后y的位置 
		 } 
	 } 
	 if(x==y) return x;//能跳到同一个点,找到最小公共祖先
	for(int i=t;i>=0;i--)//x,y同层,一起向上跳 保证一直不相等 
	{//跳到同一个节点时可能出了最小公共祖先 
		if(f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];//更新 
		}
	} 
	return f[x][0];//当前节点的父节点就是 lca
}
int main()
{
	cin>>n;
	t=log2(n)+1;
	for(int i=1;i<n;i++)
	{
		int x,y;
		cin>>x>>y;
		add(x,y);
		add(y,x);
	 } 
	cin>>u>>v;
	bfs();
	lca=LCA(u,v);
	for(int i=1;i<=n;i++)
	{
		ans=max(ans,d[i]);//最深的深度 
	}
	cout<<ans<<endl;
	ans=0;
	for(int i=1;i<=t;i++)//遍历每层节点个数 
	{
		ans=max(ans,dep[i]);
	}
	cout<<ans<<endl;
	int len=(d[u]-d[lca])*2+(d[v]-d[lca]);//此题规定的两点距离公式
	cout<<len<<endl;
	return 0; 
}

LCA详解 - TEoS - 博客园 (cnblogs.com)

例题P3884 [JLOI2009] 二叉树问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P3379 【模板】最近公共祖先(LCA) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
const int N=6e5;
int n,m,s,t,tot=0,f[N][20],d[N],ver[2*N],Next[2*N],head[N];
queue<int> q;
void add(int x,int y)
{
    ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}//邻接表存边操作。由于只求LCA时不关心边权,因此可以不存边权
void bfs()
{
    q.push(s);
    d[s]=1;//将根节点入队并标记
    while(q.size())
    {
        int x=q.front();q.pop();//取出队头
        for(int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if(d[y])
                continue;
            d[y]=d[x]+1;
            f[y][0]=x;//初始化,因为y的父亲节点就是x
            for(int j=1;j<=t;j++)
                f[y][j]=f[f[y][j-1]][j-1];//递推f数组
            q.push(y);
        }
    }
}
int lca(int x,int y)
{
    if(d[x]>d[y])
        swap(x,y);
    for(int i=t;i>=0;i--)
        if(d[f[y][i]]>=d[x])
            y=f[y][i];//尝试上移y
    if(x==y)
        return x;//若相同说明找到了LCA
    for(int i=t;i>=0;i--)
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i],y=f[y][i];
        }//尝试上移x、y并保持它们不相遇
    return f[x][0];//当前节点的父节点即为LCA
}
int main()
{
    cin>>n>>m>>s;
    t=log2(n)+1;
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    bfs();
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值