最近公共祖先~讲解
最近公共祖先(Lowest Common Ancestors):
簡稱LCA(并不是某輕型戰(zhàn)斗機(jī))
一.何為最近公共祖先
對于有根樹T的兩個(gè)結(jié)點(diǎn)u、v,最近公共祖先LCA(T,u,v)表示一個(gè)結(jié)點(diǎn)x,滿足x是u、v的祖先且x的深度盡可能大。(摘自百度百科)
那么舉個(gè)簡單的例子:看圖
那么比如14和18的lca就是3 ? 11和16的lca就是5
對于求lca,有很多種方法:tarjan啦,樹剖啦,倍增啦,暴力......
二.如何求最近公共祖先呢
首先我們想到了:暴力大法好
對于所求的兩個(gè)點(diǎn),調(diào)整到同一個(gè)深度,然后一起向上跳,這樣所求到的第一個(gè)相同的點(diǎn)就是這兩個(gè)點(diǎn)的lca。
可是暴力肯定會(huì)T...那么怎么辦呢
我們想到了之前學(xué)過的st表,利用倍增的思想來完成lca的查詢。
接著我們想到了:倍增大法好
1.首先我們準(zhǔn)備好要用到的變量
所有點(diǎn)的2^j層的父親,用fa數(shù)組保存,對于fa[i][j]表示編號為i的點(diǎn)向上跳2^j步的父親。maxlog用來枚舉j。
head,edge,cnt鄰接表的標(biāo)配,不用多說。deep[i]表示i的深度,root為所給定的根節(jié)點(diǎn)編號。add為向樹中添加一條邊。
1 const int maxlog = 20; 2 const int maxn = 550000; 3 int n, m, s; 4 int root; 5 int fa[maxn][maxlog]; 6 int deep[maxn]; 7 int head[maxn]; 8 int cnt; 9 struct Edge{ 10 int next; 11 int to; 12 }e[2*maxn]; 13 void add(int u, int v) 14 { 15 e[cnt].to = v; 16 e[cnt].next = head[u]; 17 head[u] = cnt++; 18 }2.初始化建樹,預(yù)處理每個(gè)點(diǎn)的fa數(shù)組
dfs建樹,ini初始化fa數(shù)組。
1 void dfs(int u, int p, int d) 2 { 3 fa[u][0] = p; 4 deep[u] = d; 5 for(int i = head[u]; i != -1; i = e[i].next) 6 if(e[i].to != p) dfs(e[i].to, u, d+1); 7 } 8 void init() 9 { 10 dfs (root, -1, 0); 11 for(int k = 0; k + 1 < maxlog; k++) 12 { 13 for(int v = 1; v <= n; v++) 14 if(fa[v][k] < 0) fa[v][k+1] = -1; 15 else fa[v][k+1] = fa[fa[v][k]][k]; 16 } 17 }3.接下來就是關(guān)于倍增lca的精髓所在:
(劃重點(diǎn)!)我們對于每一個(gè)點(diǎn),還是先移動(dòng)到同一高度,再開始跳。
對于第一個(gè)for便是移動(dòng)到相同深度。
對于兩個(gè)已經(jīng)跳到了相同深度的兩個(gè)點(diǎn),我們從最高處向下跳,直到遇見一個(gè)兩個(gè)點(diǎn)fa都不相同的點(diǎn),即他們的lca下面的兩個(gè)點(diǎn)。再求出這個(gè)兩個(gè)點(diǎn)的向上跳一步的父節(jié)點(diǎn)。即lca。
1 int lca(int u, int v) 2 { 3 if(deep[u] > deep[v]) swap(u, v); 4 for(int k = 0; k < maxlog; k++) 5 { 6 if(deep[v] == deep[u]) break; 7 if((deep[v] - deep[u]) >> k & 1) 8 { 9 v = fa[v][k]; 10 //cout<<deep[v]<<" "<<deep[u]<<endl; 11 }//向上跳到同樣深度 12 } 13 14 if(u == v) return u; 15 16 for(int k = maxlog - 1; k >= 0; k--) 17 { 18 if(fa[v][k] != fa[u][k]) 19 { 20 u = fa[u][k]; 21 v = fa[v][k]; 22 //cout<<u<<" "<<v<<endl; 23 } 24 25 }//從大向小跳下去 , 第一個(gè)遇到的不同的 26 return fa[u][0]; 27 }模板:
https://www.luogu.org/problemnew/show/P3379
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 const int maxlog = 20; 7 const int maxn = 550000; 8 int n, m, s; 9 int root; 10 int fa[maxn][maxlog]; 11 int deep[maxn]; 12 int head[maxn]; 13 int cnt; 14 struct Edge{ 15 int next; 16 int to; 17 }e[2*maxn]; 18 void add(int u, int v) 19 { 20 e[cnt].to = v; 21 e[cnt].next = head[u]; 22 head[u] = cnt++; 23 } 24 void dfs(int u, int p, int d) 25 { 26 fa[u][0] = p; 27 deep[u] = d; 28 for(int i = head[u]; i != -1; i = e[i].next) 29 if(e[i].to != p) dfs(e[i].to, u, d+1); 30 } 31 void init() 32 { 33 dfs (root, -1, 0); 34 for(int k = 0; k + 1 < maxlog; k++) 35 { 36 for(int v = 1; v <= n; v++) 37 if(fa[v][k] < 0) fa[v][k+1] = -1; 38 else fa[v][k+1] = fa[fa[v][k]][k]; 39 } 40 } 41 int lca(int u, int v) 42 { 43 if(deep[u] > deep[v]) swap(u, v); 44 for(int k = 0; k < maxlog; k++) 45 { 46 if(deep[v] == deep[u]) break; 47 if((deep[v] - deep[u]) >> k & 1) 48 { 49 v = fa[v][k]; 50 //cout<<deep[v]<<" "<<deep[u]<<endl; 51 } 52 } 53 54 if(u == v) return u; 55 56 for(int k = maxlog - 1; k >= 0; k--) 57 { 58 if(fa[v][k] != fa[u][k]) 59 { 60 u = fa[u][k]; 61 v = fa[v][k]; 62 } 63 } 64 return fa[u][0]; 65 } 66 int main() 67 { 68 memset(head,-1,sizeof(head)); 69 int a,b; 70 scanf("%d%d%d",&n,&m,&root); 71 for(int i = 1; i < n; i++) 72 { 73 scanf("%d%d",&a,&b); 74 add(a,b); 75 add(b,a); 76 } 77 init(); 78 for(int i = 1; i <= m; i++) 79 { 80 int u,v,a; 81 scanf("%d%d",&u,&v); 82 a = lca(u,v); 83 printf("%d\n",a); 84 } 85 return 0; 86 }?
轉(zhuǎn)載于:https://www.cnblogs.com/MisakaAzusa/p/8612942.html
總結(jié)
- 上一篇: 软件测试例子
- 下一篇: Elasticsearch和HDFS 容