预处理复杂度$O(n\ log\ n)$,查询复杂度$O(log\ n)$。

主要思想是倍增,通过预处理出夫亲的倍增数组实现快速求LCA

1 的父节点取 0 是为了方便树上差分,1 的深度设置得比 0 大是为了防止在求LCA时跳到0.

挺好理解的。

#include<bits/stdc++.h>
using namespace std;
#define file(a) freopen(#a".in","r",stdin),freopen(#a".out","w",stdout);
int n,m,s;
vector<int>head,nxt,to;
void add(int u,int v)
{
	nxt.push_back(head[u]);
	head[u]=to.size();
	to.push_back(v);
}
int f[500010][21],dep[500010];
void dfs1(int x,int fa)
{
	f[x][0]=fa;dep[x]=dep[fa]+1;
	for(int i=0;i<=19;++i)
	{
		f[x][i+1]=f[f[x][i]][i];
	}
	for(int i=head[x];i!=-1;i=nxt[i])
	{
		if(fa==to[i]) continue;
		dfs1(to[i],x); 
	}
}
int LCA(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;--i)
	{
		if(dep[f[x][i]]>=dep[y]) x=f[x][i];
		if(x==y) return x;
	}
	for(int i=20;i>=0;--i)
	{
		if(f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0];
}
int main()
{
	scanf("%d%d%d",&n,&m,&s);
	head.resize(n+1,-1);
	for(int i=1;i<=n-1;++i)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	} 
	dfs1(s,0);
	while(m--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",LCA(x,y));
	}
	return 0;
}