1. 程式人生 > 實用技巧 >【模板】最近公共祖先(LCA)

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

Description

給你一棵有根樹,1為根節點,要求你計算出指定兩個結點的最近公共祖先。

Input

輸入檔案的第一行兩個整數n和m,n為結點個數(2<=n,m<=100,000),結點編號為1到n,m表示詢問次數。
接下來n-1行,每行兩個整數x,y,表示x和y之間有一條邊相連;
接下來m行,每行兩個整數a和b,要求計算出結點a和b的最近公共祖先。

Output

輸出檔案共m行,每行為一個詢問的最近公共祖先的編號。

Sample Input

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

Sample Output

2
2


思路

  • 樹上倍增求LCA模板

程式碼

#include <iostream>
#include <cstdio>
#include <cmath>
#define maxn 100005
using namespace std;
int n,m,cnt,head[maxn],fa[maxn][17],deep[maxn];
struct node{int next,to;}e[maxn<<1];
void addedge(int x,int y){e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;}
void dfs(int u,int pre)
{
	fa[u][0]=pre; deep[u]=deep[pre]+1;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v!=pre) dfs(v,u);
	}
}
void st()
{
	for(int i=1;i<=log2(n);++i)
		for(int j=1;j<=n;++j)
			fa[j][i]=fa[fa[j][i-1]][i-1];
}
int lca(int u,int v)
{
	if(deep[u]<deep[v]) swap(u,v);
	while(deep[u]>deep[v]) u=fa[u][(int)log2(deep[u]-deep[v])];
	if(u==v) return u;
	for(int i=log2(n);i>=0;--i)
	    if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1,u,v;i<n;++i) scanf("%d%d",&u,&v),addedge(u,v),addedge(v,u);
	dfs(1,0); st();
	while(m--)
	{
		int u,v; scanf("%d%d",&u,&v);
		printf("%d\n",lca(u,v));
	}
	return 0;
}