1. 程式人生 > >最近公共祖先(Least Common Ancestors)——倍增LCA

最近公共祖先(Least Common Ancestors)——倍增LCA

ret 註意 就是 eight 有根樹 復雜 int ima n)

題目:

給定N個節點的一棵樹,有K次查詢,每次查詢a和b的最近公共祖先。

技術分享圖片

輸入

第一行兩個整數N(1 < N <= 105)、K(1 <= K <= 105)

第2~N行,每行兩個整數a、b(1 <= a,b <= N),表示a是b的父親。

第N+1~N+K+1行,每行兩個整數a、b(1 <= a,b <= N),表示詢問a和b的最近公共祖先是誰。

輸出

輸出K行,第i行表示第i個查詢的最近公共祖先是誰。

樣例輸入

16 1 1 14 8 5 10 16 5 9 4 6 8 4 4 10
1 13 6 15 10 11 6 7 10 2 16 3 8 1 16 12 16 7

樣例輸出

4 LCA就是求一棵有根樹上距離兩個點最近的公共祖先,也可以用來求兩點間的路徑。 LCA有許多做法例如離線Trajan,LCA轉RMQ等等,我只講兩個最實用的方法:樹鏈剖分和倍增LCA。 這篇先講解倍增LCA 倍增LCA是由樸素的LCA優化而來的,樸素LCA就是先選擇深度較深的點一步一步往上爬,當爬到與深度較淺的點深度相同時 兩個點同時往上爬,直到相遇為止。那麽如果樹是一條鏈,每次查詢都是O(n)的,所以我們要優化往上爬的過程,每一次成倍數(1,2,4,8……)往上爬,這樣最多爬log次就可以了。
於是我們需要預處理出來每個點往上爬2^i步到達的點是什麽。那麽轉移方程是什麽呢?我們發現對於每個點x,往上爬2^(i-1)步的點再爬2^(i-1)步就是x往上爬2^i步的點,那麽定義f[i][j]表示點i往上爬2^j步到達的點(據說把i.j調換過來會更快)f[i][j]=f[f[i][j-1]][j-1]. 但要註意的是要先從大步數往上爬,如果大的超了就不爬,這樣才能保證往上爬的時間復雜度是log級別的。
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<iostream>
 4
#include<cmath> 5 #include<cstring> 6 using namespace std; 7 int f[100001][20]; 8 int next[200010]; 9 int to[200010]; 10 int head[200010]; 11 int d[200001]; 12 int tot=0; 13 int n,m; 14 int x,y; 15 void add(int x,int y) 16 { 17 tot++; 18 next[tot]=head[x]; 19 head[x]=tot; 20 to[tot]=y; 21 } 22 void dfs(int x) 23 { 24 d[x]=d[f[x][0]]+1; 25 for(int i=1;i<=19;i++) 26 { 27 f[x][i]=f[f[x][i-1]][i-1]; 28 } 29 for(int i=head[x];i;i=next[i]) 30 { 31 dfs(to[i]); 32 } 33 } 34 int lca(int x,int y) 35 { 36 if(d[x]<d[y]) 37 { 38 swap(x,y); 39 } 40 int dep=d[x]-d[y]; 41 for(int i=0;i<=19;i++) 42 { 43 if((dep&(1<<i))!=0) 44 { 45 x=f[x][i]; 46 } 47 } 48 if(x==y) 49 { 50 return x; 51 } 52 for(int i=19;i>=0;i--) 53 { 54 if(f[x][i]!=f[y][i]) 55 { 56 x=f[x][i]; 57 y=f[y][i]; 58 } 59 } 60 return f[x][0]; 61 } 62 int main() 63 { 64 scanf("%d%d",&n,&m); 65 for(int i=1;i<=n;i++) 66 { 67 f[i][0]=i; 68 } 69 for(int i=1;i<n;i++) 70 { 71 scanf("%d%d",&x,&y); 72 f[y][0]=x; 73 add(x,y); 74 } 75 for(int i=1;i<=n;i++) 76 { 77 if(f[i][0]==i) 78 { 79 dfs(i); 80 break; 81 } 82 } 83 for(int i=1;i<=m;i++) 84 { 85 scanf("%d%d",&x,&y); 86 printf("%d\n",lca(x,y)); 87 } 88 }

最近公共祖先(Least Common Ancestors)——倍增LCA