最 近 公 共 祖 先 ( L C A ) 最近公共祖先(LCA) 最近公共祖先(LCA)
题目链接:luogu P3379
题目
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入
第一行包含三个正整数
N
N
N、
M
M
M、
S
S
S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来
N
−
1
N-1
N−1行每行包含两个正整数
x
x
x、
y
y
y,表示
x
x
x结点和
y
y
y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来
M
M
M行每行包含两个正整数
a
a
a、
b
b
b,表示询问
a
a
a结点和
b
b
b结点的最近公共祖先。
输出
输出包含 M M 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
2
2、
4
4
4的最近公共祖先,故为
4
4
4。
第二次询问:
3
3
3、
2
2
2的最近公共祖先,故为
4
4
4。
第三次询问:
3
3
3、
5
5
5的最近公共祖先,故为
1
1
1。
第四次询问:
1
1
1、
2
2
2的最近公共祖先,故为
4
4
4。
第五次询问:
4
4
4、
5
5
5的最近公共祖先,故为
4
4
4。
故输出依次为 4 4 4、 4 4 4、 1 1 1、 4 4 4、 4 4 4。
数据范围
对于
30
%
30\%
30%的数据:
N
<
=
10
,
M
<
=
10
N<=10,M<=10
N<=10,M<=10
对于
70
%
70\%
70%的数据:
N
<
=
10000
,
M
<
=
10000
N<=10000,M<=10000
N<=10000,M<=10000
对于
100
%
100\%
100%的数据:
N
<
=
500000
,
M
<
=
500000
N<=500000,M<=500000
N<=500000,M<=500000
思路
这道题是一道模板题。
为了不超时,我们得用倍增的方法来做。
每次跳
2
i
2^i
2i级,而且要从大往小跳,不然会出错。
那么,我们记录某个点的第
2
i
2^i
2i级祖先,然后就可以开始跳了。
我们先把两个点升到同一高度,在开始跳,跳到
L
C
A
LCA
LCA的下一层,然后输出其父节点就可以了。
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
struct note
{
int to,next;
}e[1000001];
int n,m,s,x,y,le[1000001],k,dep[1000001],fa[31][1000001],l[1000001];
void dfs(int father,int son)//dfs求出某个点的第2^i级祖先
{
dep[son]=dep[father]+1;
fa[0][son]=father;
for (int i=1;(1<<i)<=dep[son];i++)
fa[i][son]=fa[i-1][fa[i-1][son]];
for (int i=le[son];i;i=e[i].next)
if (father!=e[i].to)
dfs(son,e[i].to);
}
int LCA(int x,int y)//LCA
{
if (dep[x]<dep[y])
swap(x,y);//让x的深度大于等于y的深度
while (dep[x]>dep[y])
x=fa[l[dep[x]-dep[y]]-1][x];//x和y先跳到同一个深度
if (x==y) return x;//公共祖先已经找到
for (int i=l[dep[x]]-1;i>=0;i--)//向上跳
if (fa[i][x]!=fa[i][y])//不相等(还要跳)
{
x=fa[i][x];//跳
y=fa[i][y];
}
return fa[0][x];//返回其父节点(公共祖先)
}
int main()
{
scanf("%d%d%d",&n,&m,&s);//读入
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);//读入
e[++k]=(note){y,le[x]};le[x]=k;//建邻接表
e[++k]=(note){x,le[y]};le[y]=k;
}
dfs(0,s);//dfs
for (int i=1;i<=n;i++)
l[i]=l[i-1]+(1<<l[i-1]==i);//先预处理出log_2[i]+1
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);//输入
printf("%d\n",LCA(x,y));//输出
}
return 0;
}