用“倍增法”求最近公共祖先(LCA)

1.最近公共祖先:对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v
的祖先且x的深度尽可能大。
2.朴素算法:记录下每个节点的父亲,使节点u,v一步一步地向上找父亲,直到找到相同的“祖先”,即
是所求的答案,时间复杂度O(n)。
3.优化算法(倍增法):利用二进制的思想,想办法使一步一步向上搜变成以2^k地向上跳。
所以定义一个P[][]数组,使p[i][j]表示节点i的2^j倍祖先,因此p[i][0]即为i的父亲。
我们可以得到一个递推式p[i][j]=p[i][j-1][j-1]。这样子一个O(NlogN)的预处理(dfs)的 2^k 的祖先。
定义一个deep[]数组表示节点深度,
先判断是否 deep[u > deep[v]果是的话就交换一下(保证 u的深度小于 v方便下面的操作)然后把u到与
v同深度,同深度以后再把u v同时往上调(dec(j)) 调到有一个最小的j 满足: p[u] [j]!=p[v][j],u,v
是在不断更新的   最后把u,v 往上调 (u=p[u,0] v=p [v,0]) 一个一个向上调直到   u= v 这时 u or v
就是公共祖先。复杂度:O(logn)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int N,root;
 4 struct node{
 5     int rc,lc,fa,deep;
 6 };
 7 node tree[2000];
 8 int p[2000][2000];
 9 int a,b;
10 void dfs(int r);
11 int LCA(int,int);
12 int main(){
13     cin>>N;
14     for(int i=1;i<=N-1;i++){
15         int f,c;
16         cin>>f>>c;
17         if(tree[f].lc!=0) tree[f].rc=c;
18         else tree[f].lc=c;
19         tree[c].fa=f;
20         p[c][0]=f;//c的2^0==1,即它的一倍祖先是它父亲
21     }
22     for(int i=1;i<=N;i++){
23         if(tree[i].fa==0){
24             root=i;
25             break;
26         }
27     }
28     tree[root].deep=1;
29     dfs(root);
30     cin>>a>>b;
31     cout<<LCA(a,b);
32
33     return 0;
34 }
35 void dfs(int r){
36     if(r==0) return ;
37     int ll=tree[r].lc;
38     int rr=tree[r].rc;
39     tree[ll].deep=tree[r].deep+1;
40     tree[rr].deep=tree[r].deep+1;
41     for(int i=1;i<=N;i++){//处理P数组
42         int zu=1;
43         for(int k=1;k<=i;k++) zu*=2;
44         if(zu<=tree[r].deep)
45         p[r][i]=p[p[r][i-1]][i-1];
46         else break;
47     }
48     dfs(ll);
49     dfs(rr);
50 }
51 int LCA(int x,int y){
52     if(tree[x].deep<tree[y].deep) swap(x,y);//保证x比y高
53     int delta=tree[x].deep-tree[y].deep;
54     //使x和y移到统一高度
55     for(int i=0;i<=30;i++){
56         if((1<<i)&delta){//把跳的路径拆成2^k...
57             x=p[x][i];
58         }
59     }
60
61     if(x==y) return x;
62     for(int i=N-1;i>=0;i--){
63         if(p[x][i]!=p[y][i]){//保证不一样才跳,防止跳出最近祖先
64             x=p[x][i];
65             y=p[y][i];
66         }
67     }
68
69     return p[x][0];//因为不断跳到不一样的节点,所以ans一定是x或y的2^0倍祖先
70 }
时间: 2024-08-10 23:21:13

用“倍增法”求最近公共祖先(LCA)的相关文章

LCA 在线倍增法 求最近公共祖先

第一步:建树  这个就不说了 第二部:分为两步  分别是深度预处理和祖先DP预处理 DP预处理: int i,j; for(j=1;(1<<j)<n;j++) for(int i=0;i<n;++i) if(fa[i][j]=-1) fa[i][j]=fa[fa[i][j-1]][j-1];/*DP处理出i的2^j祖先是谁*/ 深度预处理: 1 void dfs(int now,int from,int deepth) 2 { 3 deep[now]=deepth; 4 for(i

求最近公共祖先(LCA)板子 x

LCA目前比较流行的算法主要有tarjian,倍增和树链剖分 1)tarjian 是一种离线算法,需要提前知道所有询问对 算法如下 1.读入所有询问对(u,v),并建好树(建议邻接表) 2.初始化每个节点各属一个并查集,都指向自己 3.对整棵树进行dfs(深度优先搜索)遍历 每处理到一个新节点(u)时看他的另一半(询问对象v)是否visit过,如果visit过了,则这组询问对的lca即v的并查集的根节点,若没有visit过,则继续向下深搜,该节点记为已visit 每当回溯的时候都将子节点的并查集

poj1330Nearest Common Ancestors以及讲解倍增法求lca

Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20487   Accepted: 10784 Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: In the figure, each node is labeled with an

倍增法求LCA

倍增法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 倍增法是通过一个数组来实现直接找到一个节点的某个祖先,这样我们就可以在O(logn)的时间内求出求出任意节点的任意祖先. 然后先把两个节点中转化为深度相同的节点,然后一起向上递增,知道找到相同的节点,该节点就是这两个节点的最近公共祖先. 代码实现: 1 #include<cstdio> 2 #include<iostream> 3 #define N

【LCA求最近公共祖先+vector构图】Distance Queries

Distance Queries 时间限制: 1 Sec  内存限制: 128 MB 题目描述 约翰的奶牛们拒绝跑他的马拉松,因为她们悠闲的生活不能承受他选择的长长的赛道.因此他决心找一条更合理的赛道.此题的输入于第一题相同,紧接着下一行输入一个整数K,以后K行为K个"距离问题".每个距离问题包括两个整数,就是约翰感兴趣的两个农场的编号,请你尽快算出这两地之间的距离. N个点,N-1条边 输入 第1行:两个分开的整数:N和M: 第2..M+1行:每行包括4个分开的内容,F1,F2,L,

最近公共祖先 LCA 倍增算法

倍增算法可以在线求树上两个点的LCA,时间复杂度为nlogn 预处理:通过dfs遍历,记录每个节点到根节点的距离dist[u],深度d[u] init()求出树上每个节点u的2^i祖先p[u][i] 求最近公共祖先,根据两个节点的的深度,如不同,向上调整深度大的节点,使得两个节点在同一层上,如果正好是祖先结束,否则,将连个节点同时上移,查询最近公共祖先. void dfs(int u){ for(int i=head[u];i!=-1;i=edge[i].next){ int to=edge[i

最近公共祖先 LCA 模板

算法步骤 时间复杂度 \(O((n+q)\log n)\),\(n\)是问题规模,\(q\)是询问个数 倍增法求\(LCA\) \(fa[i,j]\)表示从\(i\)开始向上走\(2^j\)所能到达的节点 \((0 \leq j\leq\log n)\) \(depth[i]\)表示节点\(i\)的深度 哨兵:如果从\(i\)开始跳\(2^j\)步会跳过根节点,那么\(fa[i,2^j]=0,depth[0]=0\) 先将两个节点跳到同一层 让两个节点同时往上跳,一直跳到他们最近公共祖先的下一层

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

最近公共祖先(LCA)问题

描述 对于有根树T的两个节点u和v,最近公共祖先LCA(T,u,v)表示一个节点x满足x是u,v的公共祖先且x的深度尽可能大. 算法 求解LCA问题主要有三种解法,分别是暴力搜索,Tanjar算法,最后一种是转化为RMQ问题,用DFS+ST算法来求解 暴力搜索 如果数据量不大的时候可以采用暴力搜索法.先将节点u的祖先节点全部标记出来,然后顺着节点v沿着父亲节点的方向向上遍历,直到遍历到一个被标记的节点,这个节点即为所求节点.或者分别获取u,v到根节点的路径P1,P2,可以将这两条路径看做两个两个