倍增求LCA

倍增求LCA
一,首先回顾一下什么是倍增算法,倍增算法就是改善一下一步一步跳的缓慢,改为跳2^k 步从而达到加快速度的目的,倍增算法一般要先预处理一个数组,代表从从某个点开始跳2^k 个数到达哪里,比如ST表的ST[i][j]代表从第i个数向后2^j 个数,树上倍增求LCA的f[i][j]表示i的第2^j 个祖先是谁。

二,最近公共祖先LCA概念篇
1,祖先:与x处于同一条重链且深度小于点x的节点都成为点想的祖先。
2,公共祖先:若给定一棵树,结点z即是结点x的祖先,结点z也是结点y的祖先,那么称z是结点x和y的公共祖先。
3,最近公共祖先:结点x和结点y的所有祖先中深度最深的那个,记作LCA(x,y)。

比如在此图中,5的祖先有1和2; 5和6的公共祖先有1和2; 5和6的最近公共祖先是2; 有没有发现根节点是所有任意两个节点的公共祖先,

所以最近公共祖先一定存在,最差是根节点。

三,如何求解LCA
1,考虑如何暴力求解,如果两个结点的深度相同,我们是不是只需要让两个结点同时一步一步往上走,即a走到它的父亲的同时,b也走到它的父亲,如果两个点走到的结点不相同就证明当前节点不是a和b的最近公共祖先就继续走,知道走到节点相同证明当前节点是点a和点b的最近公共祖先。
2,考虑如何优化1,1缓慢的原因是它在一步一步的向上走所以导致了算法的低效,如果我们可以大步向上走,每次在不超过范围的情况下向上走2^k步再判断是不是就加快了算法速度,为了实现这个效果我们引入倍增算法
首先要预处理两个数组Log(以2为底的对数中整数最大的那个),Log[0]=-1,Log[i]=Log[i>>1]|1;F(i, j) 表示点i 向上走2j 步的结点,Fi,0 就是点i 的父亲节点,F(i, j)= F((Fi, j−1), j−1)(从第i个点向上走2^j 个点肯定等于从i出发向上走2^(j-1) 个点再向上走2^(j-1)个节点。

算法流程:
将a 和b 调整到相同高度
a. 判断a 和b 是否重合,若重合则该点即为答案
b.令a 和b 一起向上移动尽可能大的距离,保证移动后两点不重合
c. 此时两点的父亲结点即为答案

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 500005
 6 
 7 using namespace std;
 8 
 9 struct node
10 {
11     int ed,nxt;
12 };
13 node edge[maxn<<1];
14 int first[maxn],n,m,s,cnt,f[maxn][30],Log[maxn],dep[maxn]; 
15 
16 inline void add_edge(int s,int e)
17 {
18     cnt++;
19     edge[cnt].ed=e;
20     edge[cnt].nxt=first[s];
21     first[s]=cnt;
22     return;
23 }
24 
25 inline void deal_first(int k,int fa)
26 {
27     dep[k]=dep[fa]+1;
28     f[k][0]=fa;
29     for(int i=1;(1<<i)<=dep[k];i++)
30         f[k][i]=f[f[k][i-1]][i-1];
31     for(int i=first[k];i;i=edge[i].nxt)
32     {
33         int e=edge[i].ed;
34         if(e==fa) continue;
35         deal_first(e,k);
36     }
37 }
38 
39 inline int Lca(int a,int b)
40 {
41     if(dep[a]<dep[b]) swap(a,b);
42     for(int i=Log[dep[a]];i>=0;i--)
43         if(dep[f[a][i]]>=dep[b]) a=f[a][i];
44     if(a==b) return a;
45     for(int k=Log[dep[a]];k>=0;k--)
46         if(f[a][k]!=f[b][k])
47            a=f[a][k],b=f[b][k];
48     return f[a][0];
49 }
50 
51 int main()
52 {
53     scanf("%d%d%d",&n,&m,&s);
54     for(int i=1;i<=n-1;i++)
55     {
56         int s,e;
57         scanf("%d%d",&s,&e);
58         add_edge(s,e);
59         add_edge(e,s);
60     }
61     dep[s]=1;
62     deal_first(s,0);
63     Log[0]=-1;
64     for(int i=1;i<=n;i++)
65         Log[i]=Log[i>>1]+1;
66     for(int i=1;i<=m;i++)
67     {
68         int a,b;
69         scanf("%d%d",&a,&b);
70         printf("%d
",Lca(a,b));
71     }
72     return 0;
73 }
原文地址:https://www.cnblogs.com/Hoyoak/p/11408639.html