【转】最近公共祖先(LCA)
基本概念
LCA:树上的最近公共祖先,对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。
RMQ:区间最小值查询问题。对于长度为n的数列A,回答若干询问RMQ(A,i,j),返回数列A中下标在[i,j]里的最小值下标。
朴素LCA算法
求出树上每个结点的深度。
对于查询LCA(u,v),用p1、p2指向将u、v,将p1、p2中深度较大的结点不断指向其父结点,直到p1、p2深度相同。
之后p1、p2同步向上移动,直到p1=p2,此时p1、p2所指向的结点就是LCA(u,v)。
LCA向RMQ转化
对有根树进行DFS,将遍历到的结点按顺序记录下来,将会得到一个长度为2N-1的序列,称之为T的欧拉序列F。
每个结点都在欧拉序列中出现,记录结点u在欧拉序列中第一次出现的位置为pos[u]。
记录结点u的深度为dep[u],在深度序列中记录欧拉序列中的结点的深度B[1...2N-1]。
根据DFS的性质,对于两结点u、v,从pos[u]遍历到pos[v]的过程中会经过LCA[u,v],它的深度是深度序列B[pos[u]...pos[v]]中最小的。
那么求LCA(T,u,v),就等价于求RMQ(B,u,v)。

LCA的Tarjan算法
解决LCA问题的Tarjan算法利用并查集在一次DFS(深度优先遍历)中完成所有询问。它是时间复杂度为O(N+Q)的离线算法,这里的Q表示查询次数。
算法DFS有根树T,定义从根节点到当前正在遍历的结点u的路径为活跃路径P。
对于每个已经遍历过的结点x,我们使用并查集将其连接到P上距离其最近的结点F(x)。
记录与u有关的询问集合为Q(u)。
对于Q(u)中的任意一组询问LCA(u, v),如果v已经遍历过,那么答案即为F(v)。
我们只需要维护当前所有以遍历结点的F即可。
代码流程Tarjan_DFS(u):
- 创建并查集u
- 遍历Q(u)中的所有询问(u,v),如果v已经被标记,则Answer(u,v)=v所在集合的根。
- 对于u的每一个儿子v,调用Tarjan_DFS(v)。合并u与v所在的集合,设根为u。
- 标记u。
倍增LCA
与RMQ的ST算法类似,我们令F[i][0]为结点i的第2k个父结点。
则F[i][0]为i的父结点,令w为i的第2k-1个父结点即w=F[i][k-1],那么w的第2k-1个父结点就是i的第2k个父结点即F[i][k]=F[w][k-1]。
在查询LCA时,与朴素LCA类似,先将深度较大的结点u提升到与v的深度相同,而这一次我们利用倍增法,一次提升2k个父结点,加快了算法的效率。
之后,两个结点同时提高2k(k是使2k<=dep[u]最大的正整数)。直到u、v到达同一个结点。那么这个结点就是LCA(u,v)。
倍增法的优点在于,除了能求出LCA(u,v),还可以对树上的路径进行维护。
例如要求出结点u到结点v路径上最大的边权w,我们可以在预处理F[i][k]时,用一个数组maxCost[i][k]记录结点i到它的第2k个父结点的路径上最大的边权。
那么在查询LCA(u,v)的过程中,求出u、v到公共祖先的路径上的最大边权,即u到v的路径上的最大边权。
void preprocess(){
for (int i=;i<=n;i++){
anc[i][]=fa[i];
maxCost[i][]=cost[i];
for (int j=;(<<j)<n;j++) anc[i][j]=-;
}
for (int j=;(<<j)<n;j++){
for (int i=;i<=n;i++){
if (anc[i][j-]!=-){
int a=anc[i][j-];
anc[i][j]=anc[a][j-];
maxCost[i][j]=max(maxCost[i][j-],maxCost[a][j-]);
}
}
}
}
int query(int p,int q){
int log;
if (L[p]<L[q]) swap(p,q);
for (log=;(<<log)<=L[p];log++);log--;
int ans=-INF;
for (int i=log;i>=;i--){
if (L[p]-(<<i)>=L[q]){
ans=max(ans,maxCost[p][i]);
p=anc[p][i];
}
}
if (p==q) return ans;
for (int i=log;i>=;i--){
if (anc[p][i]!=-&&anc[p][i]!=anc[q][i]){
ans=max(ans,maxCost[p][i]);
p=anc[p][i];
ans=max(ans,maxCost[q][i]);
q=anc[q][i];
}
}
ans=max(ans,cost[p]);
ans=max(ans,cost[q]);
return ans;
}
【转】最近公共祖先(LCA)的更多相关文章
- Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...
- POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)
POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...
- POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)
POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...
- [模板] 最近公共祖先/lca
简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...
- 【lhyaaa】最近公共祖先LCA——倍增!!!
高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...
- POJ 1470 Closest Common Ancestors (最近公共祖先LCA 的离线算法Tarjan)
Tarjan算法的详细介绍,请戳: http://www.cnblogs.com/chenxiwenruo/p/3529533.html #include <iostream> #incl ...
- 【Leetcode】查找二叉树中任意结点的最近公共祖先(LCA问题)
寻找最近公共祖先,示例如下: 1 / \ 2 3 / \ / \ 4 5 6 7 / \ ...
- 最近公共祖先LCA(Tarjan算法)的思考和算法实现
LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...
- 查找最近公共祖先(LCA)
一.问题 求有根树的任意两个节点的最近公共祖先(一般来说都是指二叉树).最近公共祖先简称LCA(Lowest Common Ancestor).例如,如下图一棵普通的二叉树. 结点3和结点4的最近公共 ...
- 最近公共祖先(LCA)的三种求解方法
转载来自:https://blog.andrewei.info/2015/10/08/e6-9c-80-e8-bf-91-e5-85-ac-e5-85-b1-e7-a5-96-e5-85-88lca- ...
随机推荐
- [Hadoop] Hadoop学习笔记之Hadoop基础
1 Hadoop是什么? Google公司发表了两篇论文:一篇论文是“The Google File System”,介绍如何实现分布式地存储海量数据:另一篇论文是“Mapreduce:Simplif ...
- php代码规范—2
如何写出好的 PHP 代码? 在本文中,我们将探讨一些良好的编程习惯,这将帮助你避免代码中的缺陷. 1- 编写模块化代码 良好的PHP代码应该是模块化代码.PHP的面向对象的编程功能是一些特别强大的工 ...
- 一些特殊的URI编码字符
字符 URL编码值 space %20 " %22 # %23 % %25 & %26 ( %28 ) %29 + %2B , %2C / %2F : %3A ; %3B < ...
- css-单位%号-background-size-background-position-遁地龙卷风
(-1)写在前面 我用的是chrome49,这篇是为后续做准备.重要性的调整以及毕业资料的整体导致最近没看JQuery和H5特效,以后只能晚上看了. (0)准备 div长宽都为300px,我们一张大小 ...
- jquery版小型婚礼(可动态添加祝福语)
前两天在网上不小心看到“js许愿墙”这几个字,我的神经就全部被调动了.然后就开始我的百度生涯,一直寻觅许愿墙背景图片和便利贴图片,觅了好久……一直没找到满意的……无意间看到祝福语和一些卡通婚礼图片.最 ...
- JavaScript——基本的瀑布流布局及ajax动态新增数据
本文用纯js代码手写一个瀑布流网页效果,初步实现一个基本的瀑布流布局,以及滚动到底部后模拟ajax数据加载新图片功能. 缺点: 1. 程序不是响应式,不能实时调整页面宽度: 2. 程序中当新增ajax ...
- vmware esxi 找不到网卡驱动,硬盘的解决方法
解决方法就是把ESXi无法识别的硬件的驱动定制进安装镜像文件中. ESXi 5.5 U2: VMware-VMvisor-Installer-5.5.0.update02-2068190.x86_64 ...
- [正则表达式]PCRE反向分组引用
在常见的文本匹配场景上,经常会需要用到一些像HTML这样的嵌套标签类型的文本匹配,经过多翻折腾,拼凑出了这样的一条语句 (<([\w]+)>((?1)|[\w\s])*</\2> ...
- mysql创建定时任务
一.前言 自 MySQL5.1.6起,增加了一个非常有特色的功能–事件调度器(Event Scheduler),可以用做定时执行某些特定任务(例如:删除记录.对数据进行汇总等等),来取代原先只能由操作 ...
- MVC中使用Action全局过滤器出现:网页无法正常运作 将您重定向的次数过多。解决办法
前言当我们访问某个网站的时候需要检测用户是否已经登录(通过Session是否为null),我们知道在WebForm中可以定义一个BasePage类让他继承System.Web.UI.Page,重写它的 ...