题目链接:https://www.luogu.org/problemnew/show/P3379
本题是LCA的模板,LCA大概有三种解法:DFS、倍增、tarjan。如果不了解的可以先移步:DFS+ST、倍增法。tarjan并不熟悉,所以用了倍增的模板,但是我的第一次提交超时了,去网上搜了一下发现是我保存图的结构不好。我用的是简单的邻接表,但是本题的点数多达500000,邻接表在这种情况下复杂度太高了。于是专门去学习了一种时空复杂度很好的结构:链式前向星,这种结构很好地减少了深搜的时间。而且理解起来并不困难,实现起来也很简单。代码如下:
#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn=500005;
int root,n,m,a,b,c;
//保存图结构
struct node
{
int to;
int next;
}e[2*maxn];
int head[maxn];
int cnt;
void add(int a,int b)
{
e[++cnt].to=b;
e[cnt].next=head[a];
head[a]=cnt;
}
//遍历图,求出各节点的深度,并求出fa数组
int fa[maxn][20],dep[maxn];
void dfs(int root,int pre)
{
dep[root]=dep[pre]+1;
fa[root][0]=pre;
for(int i=1;(1<<i)<=dep[root];i++)
fa[root][i]=fa[fa[root][i-1]][i-1];
for(int i=head[root];i;i=e[i].next)
{
if(e[i].to!=pre)
dfs(e[i].to,root);
}
}
//查询函数
int LCA(int a,int b)
{
if(dep[a]<dep[b])
swap(a,b);
for(int i=19;i>=0;i--)
{
if(dep[a]-(1<<i)>=dep[b])
a=fa[a][i];
}
if(a==b)
return a;
for(int i=19;i>=0;i--)
{
if(fa[a][i]!=fa[b][i])
{
a=fa[a][i];
b=fa[b][i];
}
}
return fa[a][0];
}
int main()
{
cin>>n>>m>>root;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs(root,0);
while(m--)
{
scanf("%d%d",&a,&b);
printf("%d\n",LCA(a,b));
}
return 0;
}