題目來源: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;
}