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
LCA问题的更多相关文章
- BZOJ 3083: 遥远的国度 [树链剖分 DFS序 LCA]
3083: 遥远的国度 Time Limit: 10 Sec Memory Limit: 1280 MBSubmit: 3127 Solved: 795[Submit][Status][Discu ...
- BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2050 Solved: 817[Submit][Status ...
- [bzoj3123][sdoi2013森林] (树上主席树+lca+并查集启发式合并+暴力重构森林)
Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...
- [bzoj2588][count on a tree] (主席树+lca)
Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始 ...
- [板子]倍增LCA
倍增LCA板子,没有压行,可读性应该还可以.转载请随意. #include <cstdio> #include <cstring> #include <algorithm ...
- poj3417 LCA + 树形dp
Network Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 4478 Accepted: 1292 Descripti ...
- [bzoj3626][LNOI2014]LCA
Description 给出一个$n$个节点的有根树(编号为$0$到$n-1$,根节点为$0$). 一个点的深度定义为这个节点到根的距离$+1$. 设$dep[i]$表示点$i$的深度,$lca(i, ...
- (RMQ版)LCA注意要点
inline int lca(int x,int y){ if(x>y) swap(x,y); ]][x]]<h[rmq[log[y-x+]][y-near[y-x+]+]])? rmq[ ...
- bzoj3631: [JLOI2014]松鼠的新家(LCA+差分)
题目大意:一棵树,以一定顺序走完n个点,求每个点经过多少遍 可以树链剖分,也可以直接在树上做差分序列的标记 后者打起来更舒适一点.. 具体实现: 先求x,y的lca,且dep[x]<dep[y] ...
- 在线倍增法求LCA专题
1.cojs 186. [USACO Oct08] 牧场旅行 ★★ 输入文件:pwalk.in 输出文件:pwalk.out 简单对比时间限制:1 s 内存限制:128 MB n个被自 ...
随机推荐
- 标准MD5加密算法
标准MD5加密算法: public class Md5 { public static String getMd5(String s) { char hexDigits[] = { '0', '1', ...
- 基于Maven的spring_security入门
配置文件的修改点没什么变化,可以参考:http://blog.csdn.net/ouitiken/article/details/8830505 pom.xml的依赖参考: <dependenc ...
- ASP.NET多用户操作相同互斥的对象
[一篮饭特稀原创,转载请注明出自http://www.cnblogs.com/wanghafan/p/3574154.html ] 现象:公有静态变量不可用于ASP.NET多用户操作,否则该变量会被多 ...
- u-boot使用
下载与烧写 使用U-boot将映像文件烧写到板上的Flash,一般步骤是: (1)通过网络.串口.U盘.SD卡等方式将文件传输到SDRAM: (2)使用Nand Flash或Nor Flash相关的读 ...
- 拷贝出你GAC里面的DLL文件
转:http://blog.sina.com.cn/s/blog_573be9bc0100ht65.html 早上和同事探讨如何从GAC中拷贝已经部署的DLL文件的时候,除了以前熟悉的命令行拷贝方法, ...
- Eclipse无法识别Android真机的解决方法
使用IORegistryExplorer(在Developer Tool)可以查看连接上的USB设备的信息.选择IOUSB,可以看到名为连线的安卓设备,比如小米idVendor是0x2717. ech ...
- HDOJ/HDU 1556 Color the ball(树状数组)
Problem Description N个气球排成一排,从左到右依次编号为1,2,3-.N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"牌电动车从 ...
- [洛谷U990]传递游戏(90分)
[题目描述 Description] n个人在做传递物品的游戏,编号为1-n. 游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位:下一个人可以传递给未接过物品的任意一 ...
- JavaScript高级程序设计42.pdf
IE中的事件对象 要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法.在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在 wondow.e ...
- MySQL WorkBench中文教程
在网上找到了一份MySQL WorkBench的教程,点此可以下载Work Bench教程(原文),为了便于学习和交流,请朋友帮忙翻译成了中文,点此可以下载Work Bench教程(中文翻译版). 具 ...