难度 普及+/提高
题目描述
给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入输出格式
输入格式
第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。
输出格式
输出包含M行,每行包含一个正整数,依次为每一个询问的结果。
输入输出样例
输入样例
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
输出样例
4
4
1
4
4
样例说明
该树结构如下:
第一次询问:2、4的最近公共祖先,故为4。
第二次询问:3、2的最近公共祖先,故为4。
第三次询问:3、5的最近公共祖先,故为1。
第四次询问:1、2的最近公共祖先,故为4。
第五次询问:4、5的最近公共祖先,故为4。
故输出依次为4、4、1、4、4。
代码
#include<bits/stdc++.h>
#define MN 500005
using namespace std;
int m,n,s,tot,dep[MN],up[MN][20];
struct lll
{
int to;
lll *ne;
}a[2*MN];
lll *head[MN];
void DFS(int u)
{
for(int i=1;(1<<i)<=n;i++)
{
up[u][i]=up[up[u][i-1]][i-1];
}
for(lll *e=head[u];e;e=e->ne)
{
if(dep[e->to]==-1)
{
dep[e->to]=dep[u]+1;
up[e->to][0]=u;
DFS(e->to);
}
}
return;
}
int LCA(int u,int v)
{
if(dep[v]>dep[u]) swap(u,v);
for(int i=19;i>=0;i--)
{
if(dep[up[u][i]]>=dep[v])
u=up[u][i];
}
if(u==v) return v;
for(int i=19;i>=0;i--)
{
if(up[u][i]!=up[v][i])
{
u=up[u][i];
v=up[v][i];
}
}
return up[u][0];
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[++tot].ne=head[x];
head[x]=&a[tot];
a[tot].to=y;
a[++tot].ne=head[y];
head[y]=&a[tot];
a[tot].to=x;
}
memset(dep,-1,sizeof(dep));
dep[s]=0;
up[s][0]=0;
DFS(s);
for(int j=1;j<=m;j++)
{
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",LCA(u,v));
}
return 0;
}