【Black-Panda】LCA最近公共祖先 學習筆記
阿新 • • 發佈:2022-04-05
1. 定義:
LCA(Least Common Ancestors),即最近公共祖先,是指在有根樹中,找出某兩個結點 \(x\) 和 \(y\) 最近的公共祖先(深度最大的祖先),記為:\(LCA(x,y)\)。
舉例:
- \(LCA(15,12)=4\)
- \(LCA(10,12)=10\)
圖例:
作用:能在 \(log(n)\) 解決從 \(u\) 到 \(v\) 的路線問題。
2. 求解:
方法一:向上標記法(暴力求解)
-
從 \(x\) 向上走到根節點,並標記所有走過的結點。
-
從 \(y\) 走到根,當第一次遇到有標記的結點時,就找到了 \(LCA(x,y)\)。
-
最壞時間 \(O(n)\)
-
給出程式碼片段:
int LCA(int x,int y){
while(x>0)vis[x]=1,x=fa[x];
while(y>0 && !vis[y])y=fa[y];
return y;
}
方法二:暴力優化
-
用 \(fa[i]\) 記錄 \(i\) 的父親結點。
-
首先將 \(x\) 和 \(y\) 中深度較深的那個點跳到和較淺的點同樣的深度。
-
然後兩個點一起一步一步向上跳,直到跳到同一個點 \(p\),就是它們的 \(LCA\)。
-
複雜度:最壞情況 \(O(o)\),適合只計算一次 \(LCA(x,y)\)。
\(p=LCA(x,y)\)
-
結點深度:\(d[x],d[y]\)
-
\(x\) 向上走 \(d[x]-d[p]\)
\(y\) 向上走 \(d[y]-d[p]\)
-
實現方法:\(u\) 和 \(v\) 深度大的向上走 \(|d[x]-d[y]|\),再一起一步一步向上走,直到走到同一個結點 \(p\)。
-
時間:\(O(d[x]+d[y])\)
-
儲存:有向圖的鄰接表
vector<int> G[maxn];
int fa[maxn];
int d[maxn];
- 深度優先遍歷,同時求 \(fa[]\)
\(dfs(root,1);\)
void dfs(int u,int depth){ d[u]=depth; int m=G[u].size(); for(int i=0;i<m;i++){ int v=G[u][i]; dfs(v,depth+1); } }
- 參考程式碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
vector<int> G[maxn];
int fa[maxn],d[maxn],n,m,s;
void dfs(int u,int p,int depth){
d[u]=depth;
fa[u]=p;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v!=p) dfs(v,u,depth+1);
}
}
int LCA(int x,int y){
while(d[x]>d[y]) x=fa[x];
while(d[x]<d[y]) y=fa[y];
while(x!=y){
x=fa[x];y=fa[y];
}
return x;
}
int main(){
scanf("%d%d%d",&n,&m,&s);
int u,v;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(s,-1,1);
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
printf("%d\n",LCA(u,v));
}
return 0;
}
方法三:二進位制拆分思想(倍增)
求 \(LCA(x,y)\) 的步驟:
- 設 \(d[x]\) 表示 \(x\) 的深度。假設 \(d[x]>=d[y]\) (否則可以交換 \(x\) 和 \(y\))。
- 利用二進位制拆分思想,把 \(x\) 向上調整到 \(y\) 同一高度。
- \(x\) 向上走 \(i=2^{log_n},...,2^1,2^0\) 步,檢查 \(x\) 到達的節點是否比 \(y\) 深,若是則 \(x=fa[x][i]\)。
- 如果 \(x=y\),\(LCA(x,y)=y\)。
- 利用二進位制思想,\(x\) 和 \(y\) 同時往上跳,並保持深度一致且二者\(\color{#ff0000}{\text{不相遇}}\)。
- \(x\) 和 \(y\) 同時向上走 \(i=2^{log_n},...,2^1,2^0\) 步。
- 如果 \(fa[x][i]!=fa[y][i]\),則 \(x=fa[x][i],y=fa[y][i]\)。
- 此時只差一步就得相遇了,它們的父親節點 \(fa[x][0]\)(或\(fa[y][0]\)),就是 \(LCA(x,y)\)。
3.推薦
bilibili:BV1nE411L7rz