最近公共祖先(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

POJ 1330 Nearest Common Ancestors(最近公共祖先 Tarjan离线)