题目来源:https://www.luogu.org/problem/P3379
★第一道LCA题,看了半天,这是一道入门滴模板题
LCA有很多解法,本题仅用 倍增法求LCA (我目前只会这一种 )
思路:
仔细地看样例,可以看明白什么是LCA了吧。
先说说直观的暴力思路:
每个点有一个dep,代表 深度 。现在要求 LCA(a,b ,那么如果 dep(a)<dep(b) ,则交换 a和b(保证 dep[a]>=dep[b] )。然后a往上跳(a=a的祖先),一直到 dep[a]=dep[b] 最后一起往上跳,直到他们的祖先相同。
优化方法:
如果我们在跳的过程中可以 一次跳多步 ,那效率肯定会高很多,这就是 树上倍增法
首先 fa[ i ][ j ] 表示 i 的 第 2j 辈的祖先(从i往根节点的方向走 2j 步 到达的点,若不存在则为0),可知fa[ i ][ 0 ]表示父节点。
可以得到递推式 fa[ i ][ j ]=fa[ fa[ i ][ j-1 ] ][ j-1 ] 由此 可以从根节点推出全部的值
预处理部分差不多就是这样~
还想优化的话 可以加一个 lg数组 lg[ i ] 代表 log2(i)+1 递推如右:lg[i]=lg[i-1]+(1<<lg[i-1]==i);
详见代码
其他方法 如 tarjan rmq 可见大佬博客:https://blog.csdn.net/ywcpig/article/details/52336496
代码:
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<deque>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=5e5+5;
const int sz=1<<16;
const int inf=2e9;
const int mod=1e9+7;
const double pi=acos(-1);
typedef long long LL;
int n,m,cnt;
struct node
{
int next;
int to;
}edge[maxn<<1];
int head[maxn],dep[maxn];
int fa[maxn][22],lg[maxn];
template<class T>
inline void read(T &x)
{
char c;x=1;
while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
T res=c-'0';
while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
x*=res;
}
void add_edge(int a,int b) //链式前向星
{
edge[++cnt].to=b;
edge[cnt].next=head[a];
head[a]=cnt;
}
void dfs(int p,int f) //预处理
{
// cout<<p<<'x'<<endl;
dep[p]=dep[f]+1;
fa[p][0]=f;
for(int i=1;(1<<i)<=dep[p];i++){
fa[p][i]=fa[fa[p][i-1]][i-1];
}
for(int i=head[p];i;i=edge[i].next){
if(edge[i].to!=f) //不能等于父节点
dfs(edge[i].to,p);
}
}
int lca(int a,int b)
{
if(dep[a]<dep[b]) swap(a,b); //保证 a深度大
while(dep[a]>dep[b]){ //a往上跳
a=fa[a][lg[dep[a]-dep[b]]-1];
}
if(a==b) return a;
for(int i=lg[dep[a]]-1;i>=0;i--){
if(fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i]; //a b 一起往上跳
}
return fa[a][0]; //注意返回的是 a的父节点
}
int main()
{
int s,a,b;
cnt=0;
read(n); read(m); read(s);
for(int i=1;i<n;i++){
read(a); read(b);
add_edge(a,b);
add_edge(b,a);
}
dfs(s,0);
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
while(m--){
read(a); read(b);
printf("%d\n",lca(a,b));
}
return 0;
}