lca算法
今天是个好日子 2022 - 7-15(萌新,如果有错误的地方还请指正QAQ
lca是一种可以快速在线求出一个树上的两个点的最近公共祖先,其思路是先预处理出来每个点的深度,然后用倍增的思想,判断每个节点向上跳2的i次方的点会到哪个位置(类似于二进制优化的思想,这样可以拼凑出任何一个数)。前方的操作预处理过后就开始查询了
预处理每一个点的深度,和求出每一个点向上跳的距离所对应的点。
注意一个点,在求得深度时,我们会在根节点的上方多设置一个节点0,这样的目的是为了在向上跳的时候,因为向上跳的时候枚举的是从大到小的2的n次方可以向上跳的高度,而如果这个跳跃的高度太大的话,就会到一个初始值0,而两个点同时向上跳时,我们使循环结束的条件是:两个点再向上跳一个点就会到他们的最近公共祖先,所以我们需要 设置一个哨兵来在逻辑上满足这个条件。在求深度的时候使用的是bfs
-
首先将所有点的深度置为正无穷(0x3f3f3f3f),并设置depth[0] = 0 (哨兵,防止跳跃的太大而出界) 在将depth[root] 置为1.
-
首先先将根结点加入到队列当中,然后走通常的bfs格式。
-
拿出队首t ,枚举 t 的所有边,如果说相连的那条边所对应的点 J 深度小于当前点的深度+1,就将j的深度置为depth[t]+1,并将该点加入到队列当中,然后设置他的向上跳2^0次所到达的地方为t 即 f【j】【0】 = t ; 然后枚举 j 可以向上跳的所有点。因为是从根向下遍历,所以可以直接用上边已经求得的数据来更新当前的j点。并且有一个性质为 f 【a】【b】 = f【f【a】【b-1】】【b-1】,我们直接在逻辑上理解这个等式。当前点向上跳2^b 次方次 就相当于当前点 先向上跳 2^b-1次方到的点 now , 再从now向上跳2^b-1次方到达的点。这个过程就是向上跳的过程。由于是b-1,所以b枚举时需要从1开始枚举。这个过程完之后,处理完所有点,预处理就完成了
-
首先先将两个点移动到同一层:先将深度深的点A一直向上跳,直到与B点的深度相同,然后判断当前点是否是同一个,如果是同一个的话,点B就就是两个节点的公共祖先。
-
在判断,如果当前两个点在同一层之后并不在同一个点,则同时跳A和B,直到两个点还有一个点就到最近公共节点的时候停止,这时候只需要返回他们的父结点即可。
下面就是代码和题的链接
链接是洛谷的的题lca板子题
下面是代码块
#include<cstring> #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<queue> using namespace std; const int N = 500010, M = 1000010; int h[N],e[M],ne[M],idx; int depth[N]; int fa[N][21]; int n , m ; void add(int a, int b ) { e[idx] = b, ne[idx] = h[a] , h[a] = idx ++ ; } void bfs(int root) { memset(depth,0x3f,sizeof depth); depth[0] = 0 ;//哨兵 depth[root] = 1; queue<int> q; q.push(root); while(q.size()) { auto t = q.front(); q.pop(); int len = depth[t] + 1; for(int i = h[t] ; ~i ; i = ne[i]) { int j = e[i]; if(depth[j] > len) { depth[j] = len; q.push(j); fa[j][0] = t; for(int b = 1; b <= 19 ; b++) { fa[j][b] = fa[fa[j][b-1]][b-1]; } } } } } int find(int a, int b) { if(depth[a] < depth[b])swap(a,b); for(int i = 19; i >= 0 ; i --) if(depth[fa[a][i]] >= depth[b])a = fa[a][i];//跳到同一层。 if(a == b)return b ;//如果其中一个是另一个的祖先直接返回。 for(int i = 19 ; i >= 0 ; i --)//两个同时跳。 { if(fa[a][i] != fa[b][i]) { a = fa[a][i]; b = fa[b][i]; } } return fa[a][0];//返回当前结点的父结点。 } int main() { int root; memset(h,-1,sizeof h); cin >> n >> m >> root; int a, b ; for(int i = 1; i <= n-1 ; i ++) { cin >> a >> b ; add(a,b); add(b,a); } bfs(root); while(m--) { int l ,r ; cin >> l >> r; cout << find(l,r)<<endl; } return 0 ; }