最近公共祖先(Least Common Ancestors)——倍增LCA
Posted khada-jhin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最近公共祖先(Least Common Ancestors)——倍增LCA相关的知识,希望对你有一定的参考价值。
题目:
给定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的主要内容,如果未能解决你的问题,请参考以下文章
最近公共祖先 · Lowest Common Ancestor
POJ 1330 Nearest Common Ancestors 最近公共祖先
[最近公共祖先] POJ 1330 Nearest Common Ancestors
最近公共祖先 Lowest Common Ancestors
LeetCode 235. 二叉搜索树的最近公共祖先(Lowest Common Ancestor of a Binary Search Tree) 32