

LCA最近公共祖先 - little_sheep_xiaoen
source link: https://www.cnblogs.com/xiao-en/p/16460460.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

LCA最近公共祖先
最近公共祖先就字面意思,两个节点一起往上跳,找到的最近的公共点
找到u和v第一个不同祖先不同的位置,然后这个位置向上走一步就是最近公共的祖先
但是想找到u,v第一个不同祖先的位置,就要保证u,v在同一深度(才能一起往上移动)
所以这个过程分为三部分,
1. 预处理找到每个节点深度
2. 把较深的一点移动到较浅一点的高度
3. 两个一起往上移动直到他们的父亲相同
预处理找深度
这里找深度可以用一个deepdeep数组存下,用bfsbfs找到所有深度,顺便可以把父节点也记录了
BFS找深度与父节点
找公共祖先
在两点跳到同样深度后,有两种做法
(一)一步一步暴力跳
(二)倍增
当然用倍增啦~
那我们就预处理一下a[i][j]a[i][j],记录每个节点ii往上跳2j2j步后,跳到的祖先是谁
因为ii移动2j2j次就相当于从i移动2j−12j−1次后再移动2j−12j−1次 找到状态转移方程 $father [ i ] [ j ] = father [ father [ i ] [ j -1] ] [ j-1 ] ;$
然后用dpdp做一个预处理(在bfsbfs时查到这个点就处理这个点)
倍增预处理
之后就开始跳就完了
int LCA( int x,int y ){ if( deep[x] < deep[y] ) swap( x,y ); //默认x更深 int h = deep[x] - deep[y];//取出高度差 for( int i = 0;i <= 20;i++ ) //保证每个都能测到 if( h & (1<<i) ) //二进制跳高度 x = a[x][i]; if( x == y ) return y; //如果y是x的祖先,返回y就行 for( int i = 20;i >= 0;i-- ){ //找到第一个不相同的节点 if( a[x][i] != a[y][i] ){ x = a[x][i]; y = a[y][i]; } } return a[x][0]; //公共祖先就是第一个不相同的点的上一个点 }
代码如下:
#include<iostream> #include<cstdio> #include<queue> #define NUM 500010 using namespace std; int n,m,s; int fa[NUM],deep[NUM]; int a[NUM][23]; struct bian{ int next,to; }; bian e[NUM<<1]; int head[NUM]; bool vis[NUM]; int cnt; void add( int x,int y ){ e[++cnt].next = head[x]; e[cnt].to = y; head[x] = cnt; } void st( int p ){ a[p][0] = fa[p]; for( int i = 1;i <= 20;i++ ) a[p][i] = a[ a[p][i-1] ][i-1]; } void bfs( int s ){ queue <int> fat,step; fa[s] = s;deep[s] = 1; for( int i = 0;i <= 20;i++ ) a[s][i] = s; fat.push(s);step.push(1); int nf,ns; vis[s] = 1; while( !fat.empty() ){ nf = fat.front(); ns = step.front(); fat.pop();step.pop(); for( int i = head[nf];i;i = e[i].next ){ int to = e[i].to; if( vis[to] ) continue; fa[to] = nf; deep[to] = ns+1; vis[to] = 1; st(to); fat.push(to);step.push(deep[to]); } } } int LCA( int x,int y ){ if( deep[x] < deep[y] ) swap( x,y ); int h = deep[x] - deep[y]; for( int i = 0;i <= 20;i++ )
if( h & (1<<i) ) x = a[x][i];
if( x == y ) return x; for( int i = 20;i >= 0;i-- ){ if( a[x][i] != a[y][i] ){ x = a[x][i]; y = a[y][i]; } } return a[x][0]; } int main(){ cin >> n >> m >> s; int x,y; for( int i = 1;i <= n-1;i++ ){ cin >> x >> y; add( x,y ); add( y,x ); } bfs( s ); // for( int i = 1;i <= n;i++ ) // for( int i = 1;i <= n;i++ ){ // printf( "\n节点i = %d,父亲为%d,深度为%d\n",i,fa[i],deep[i] ); // for( int j = 0;j <= 20;j++ ) // printf( " 往上%d下,为%d\n",j,a[i][j] ); // } for( int i = 1;i <= m;i++ ){ cin >> x >> y; int p = LCA(x,y); // if( p == 0 || p == -1 ) cout << s << endl; // else cout << p << endl;; } return 0; }
Warning:加黄部分要注意,确实要这么写
如果写成如下形式则WA
int cnt = 0; while( h&1 ){ x = a[x][cnt]; cnt++; h >>= 1; }
因为如果hh的二进制为1101011010,则whilewhile会退出循环
Recommend
-
19
ARTS ARTS 是陈浩(网名左耳朵耗子)在极客时间专栏里发起的一个活动,目的是通过分享的方式来坚持学习。 每人每周写一个 ARTS:Algorithm 是一道算法题,Review 是读一篇英文文章,Technique/Tips 是分享一个小技术...
-
8
二叉树的最近公共祖先 👆让天下没有难刷的算法!若 GitBook 访问太慢,可尝试
-
8
好久没刷题了,复习一下:LCA。 题目很简单,就是求多叉树两个点的最近公共祖先。 链接: 洛谷 P3379 LCA(Least Common Ancestors),即最近公共祖先,是指在有根树...
-
8
Leetcode 236 二叉树的最近公共祖先(Lowest Common Ancestor of a Binary Tree) 题解分析 Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According to th...
-
4
大家好,我是 lucifer。今天给大家分享 Git 中的算法。 这是本系列的第二篇 - 《Git 中的最近公共祖先》,第一篇在这里 git merge-basegit merge-base A B...
-
5
LeetCode学习笔记——二叉树的最近公共祖先 阅读数:22次
-
4
研究了LCA,写篇笔记记录一下。 讲解使用例题 P3379 【模板】最近公共祖先(LCA)。 什么是LCA ...
-
8
二叉树两个节点的最近公共祖先问题 作者:Grey 原文地址:
-
5
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 什么是最近公共祖先呢? 百度百科解释 中将最近公共...
-
6
1 需求实现 绘制魔方 中基于OpenGL ES 实现了魔方的绘制,实现较复杂,本文基于 Unity3D 实现了 2 ~ 10 阶魔方的整体旋转和局部旋转。
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK