目录
Description
给出N,Q .N<=5*10^5 代表一个树有N个点 ,树的根为1 Q代表有Q个询问,询问A,B的最近公共祖先是哪一个.
Format
Input
第一行给出N,Q 下面N-1行描述这个树,格式为A,B,代表A为B的Son ,接来下Q行 给出A,B,代表进行询问A,B的LCA是哪一个.
Output
针对每个询问输出结果
Samples
输入数据 1
4 2
2 1
3 2
4 2
3 4
4 2
输出数据 1
2
2
提示 请使用printf语句进行输出.用cout会TLE
分析
首先,这些多组数据题一般是用来卡时间的
既然知道了,那么就要想一想该怎么办。
这里我们会用到一个叫做“倍增”的算法。
大家可以看看这篇文章:
https://blog.csdn.net/chengzhang41103/article/details/123600247https://blog.csdn.net/chengzhang41103/article/details/123600247在这篇文章中,我们在线性结构上,从一次跳一步变成了一次跳步。
那么为什么不在树上也这样做呢?毕竟树也是由线性结构组成的。
接下来进入正题
首先,每个点跳步就是它的父亲点
而跳步就等于先跳步再跳步
那么递归公式就是: 。
预处理完以后,就可以开始lca了。
先dfs一遍,把每个点深度求出来。
注意:根节点的深度为1,因为根节点的上方在dfs时又会出现一个0号节点,而深度不应为负数。
然后,得到两个点:假如他们是x,y
首先,确保x深度更深。用i逆循环从20到1,用f数组向上跳,
只要x跳步后深度依然大于y
就使x=f[x][i]。
第二步。倘若跳完后,x==y,那么lca就是x或y了
否则,两个点一起往上跳,只要不跳到同一个点上就行(why?先想一想,答案在代码中)。
经过第二步后,x就跳到了lca的下面,输出f[x][0]就可以了
另外,在代码实现时,还有一些小细节。题目中给出的条件一定要用上。
看到题目中那行加黄的字了吗?那可是原题中的。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1000100;
int front[N],last[N],kid[N];
int flor[N],f[N][21];
int n,x,y,num,q,o;
void put(int x,int y)
{
num++;
front[num]=last[x];
last[x]=num;
kid[num]=y;
}
void did(int father,int child,int dep)
{
flor[child]=dep;
for(int i=last[child];i;i=front[i])
if(father!=kid[i])
did(child,kid[i],dep+1);
}
int lca(int x,int y)
{
if(flor[x]<=flor[y])
swap(x,y);
for(int i=20;i>=0;i--)
if(flor[f[x][i]]>=flor[y])
x=f[x][i];
if(x==y)
return x;
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
//如果跳到同一个点上,则一定是公共祖先,但不一定是lca
{
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
int main()
{
cin>>n>>q;
for(int i=1;i<n;i++)
{
cin>>x>>y;
put(y,x);
f[x][0]=y;
}
did(0,1,1);
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
for(int i=1;i<=q;i++)
{
cin>>x>>y;
o=lca(x,y);
printf("%d\n",o);
}
}