倍增求LCA

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

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式

第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数、询问的个数和树根结点的序号。

接下来 \(N-1\) 行每行包含两个正整数 \(x, y\),表示 \(x\) 结点和 \(y\) 结点之间有一条直接连接的边(数据保证可以构成树)。

接下来 \(M\) 行每行包含两个正整数 \(a, b\),表示询问 \(a\) 结点和 \(b\) 结点的最近公共祖先。

输出格式

输出包含 \(M\) 行,每行包含一个正整数,依次为每一个询问的结果。

样例 #1

样例输入 #1

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

样例输出 #1

4
4
1
4
4

提示

对于 \(30\%\) 的数据,\(N\leq 10\)\(M\leq 10\)

对于 \(70\%\) 的数据,\(N\leq 10000\)\(M\leq 10000\)

对于 \(100\%\) 的数据,\(N\leq 500000\)\(M\leq 500000\)

样例说明:

该树结构如下:

第一次询问:\(2, 4\) 的最近公共祖先,故为 \(4\)

第二次询问:\(3, 2\) 的最近公共祖先,故为 \(4\)

第三次询问:\(3, 5\) 的最近公共祖先,故为 \(1\)

第四次询问:\(1, 2\) 的最近公共祖先,故为 \(4\)

第五次询问:\(4, 5\) 的最近公共祖先,故为 \(4\)

故输出依次为 \(4, 4, 1, 4, 4\)

2021/10/4 数据更新 @fstqwq:应要求加了两组数据卡掉了暴力跳。

代码

// Problem: P3379 【模板】最近公共祖先(LCA)
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3379
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//fw
#include<bits/stdc++.h>
#define pii pair <int, int>
#define pll pair <ll, ll>
#define endl '\n'
#define il inline
#define pb push_back
#define fi first
#define se second
#define lc u<<1
#define rc u<<1|1
using namespace std;
typedef long long ll;
const int N=5e5+5,M=2*N;//开双向边
int h[N],e[M],ne[M],idx;//前向星
int fa[N][25],q[N];//fa[i][j]从i开始跳2^j步跳到的点
int n,m,root;
int depth[N];//每个点的深度,用于把a,b跳到同一层
void add(int a,int b)//前向星加边
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
void bfs()
{
	memset(depth,0x3f,sizeof depth);//深度初始化
	depth[0]=0;depth[root]=1;//定义0号点为哨兵,防止跳过头
	q[0]=root;
	int hh=0,tt=0;
	while(hh<=tt)//宽搜计算深度
	{
		int t=q[hh++];
		for(int i=h[t];~i;i=ne[i])
		{
			int j=e[i];
			if(depth[j]>depth[t]+1)
			{
				depth[j]=depth[t]+1;
				q[++tt]=j;
				
				fa[j][0]=t;//倍增求fa,与st表类似
				for(int k=1;k<=20;k++)
				{
					fa[j][k]=fa[fa[j][k-1]][k-1];
				}
			}
		}
	}
}
int lca(int a,int b)
{
	if(depth[a]<depth[b])swap(a,b);//用a表示较深的点
	for(int k=20;k>=0;k--)//把a和b跳到同一层
	{
		if(depth[fa[a][k]]>=depth[b])
		a=fa[a][k];
	}
	if(a==b)return a;//如果跳到同一层后相等,则说明这个点就是最近公共祖先
	
	for(int k=20;k>=0;k--)//如果不相等则,两个点一起往上跳
	{
		if(fa[a][k]!=fa[b][k])//如果跳过头或者两个点的父节点是一个点则不跳
		{
			a=fa[a][k];
			b=fa[b][k];
		}
	}
	return fa[a][0];//返回父节点即lca
}
int main()
{

	memset(h,-1,sizeof h);//前向星初始化
	
	cin>>n>>m>>root;
	
	for(int i=1;i<=n-1;i++)
	{
		int a,b;
		cin>>a>>b;
		add(a,b),add(b,a);
	}
	
	bfs();//预处理出depth和fa
	
	while(m--)//处理每组询问
	{
		int a,b;
		cin>>a>>b;
		cout<<lca(a,b)<<endl;
	}
	
  return 0;
}
posted @ 2022-06-28 11:15  Avarice_Zhao  阅读(9)  评论(0编辑  收藏  举报