LCA(最近公共祖先)
学习链接:https://baike.baidu.com/item/%E4%BC%B8%E5%B1%95%E6%A0%91/7003945?fr=aladdin
求LCA的方法有很多,在这里就只介绍一种离线算法,Tarjan算法! 可以保证在O(n+q)的时间复杂度内算出所有答案(n是节点个数 q是询问个数)
为什么叫离线算法呢? 因为这种算法的思想是先把所有的询问存起来,在遍历的树的同时遍历这颗子树包含的询问:
本算法结合了深度优先搜索(DFS)和并查集的思想,下面说一下大体流程:
对于新搜索的到的结点,首先要创建由这个节点构成的集合,再对当前节点的每一颗子树进行搜索,每搜索完一颗子树要保证子树内的LCA询问都已解决。其它的LCA必定在
这个子树之外,这时把子树所形成的的集合与当前节点的集合合并,并把当前节点设为这个集合的祖先 。之后继续搜索下一颗子树,直到当前节点的所有子树搜索完。这时把当前节点也设为已经访问过的结点
同时处理有关当前节点的询问,如果有一个当前节点到节点v的询问,且被检查过,则由于进行的是深度优先搜索,当前节点与v的最近公共祖先一定还没有被检查过,而这个最近公共祖先的包含v的子树
一定已经搜索过了,那么这个最近公共祖先一定是v集合所在集合的祖先。

根据实现算法可以看出,只有当某一棵子树全部遍历处理完成后,才将该子树的根节点标记为黑色(初始化是白色),假设程序按上面的树形结构进行遍历,首先从节点1开始,然后递归处理根为2的子树,当子树2处理完毕后,节点2, 5, 6均为黑色;接着要回溯处理3子树,首先被染黑的是节点7(因为节点7作为叶子不用深搜,直接处理),接着节点7就会查看所有询问(7, x)的节点对,假如存在(7, 5),因为节点5已经被染黑,所以就可以断定(7, 5)的最近公共祖先就是find(5).ancestor,即节点1(因为2子树处理完毕后,子树2和节点1进行了union,find(5)返回了合并后的树的根1,此时树根的ancestor的值就是1)。有人会问如果没有(7, 5),而是有(5, 7)询问对怎么处理呢? 我们可以在程序初始化的时候做个技巧,将询问对(a, b)和(b, a)全部存储,这样就能保证完整性。
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=;//最大顶点数
int n,root;//实际顶点数 树根节点
int indeg[maxn];//顶点入度 用来判断树根
vector<int> tree[maxn];//树的邻接表(不一定是二叉树)
void inputTree()
{
scanf("%d",&n);
for(int i=;i<n;i++) tree[i].clear(),indeg[i]=;//初始化树 顶点编号从0开始
for(int i=;i<n;i++)//输入n-1条边
{
int x,y;
scanf("%d%d",&x,&y);
tree[x].push_back(y);//x->y有一条边
indeg[y]++;//加入邻接表 y入度加一
}
for(int i=;i<n;i++)//寻找树根 入度为0的点
{
if(indeg[i]==)
{
root=i;
break;
}
}
}
vector<int> query[maxn];//所有查询内容
void inputQuires()//输入查询
{
for(int i=;i<n;i++)//清空上次查询的内容
query[i].clear();
int m;
scanf("%d",&m);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);//查询u和v的lca
query[u].push_back(v);
query[v].push_back(u);
}
}
int father[maxn],rnk[maxn];//节点的父亲 秩
void makeSet()//初始化并查集
{
for(int i=;i<n;i++)
{
father[i]=i;
rnk[i]=;
}
}
int findSet(int x)//查找祖先
{
return father[x]==x?x:father[x]=findSet(father[x]);
}
void unionSet(int x,int y)//合并
{
x=findSet(x);
y=findSet(y);
if(x==y) return;
if(rnk[x]>rnk[y]) father[y]=x;
else
{
father[x]=y;
if(rnk[x]==rnk[y]) rnk[y]++;
}
}
int ancestor[maxn];//已经访问节点集合的祖先
bool vs[maxn];//访问标记
void Tarjan(int x)//Tarjan算法求解lca
{
for(int i=;i<tree[x].size();i++)
{
Tarjan(tree[x][i]);//访问子树
unionSet(x,tree[x][i]);//将子树节点与根节点x的集合合并
ancestor[findSet(x)]=x;//合并后的集合的祖先为x
}
vs[x]=;//标记为访问过
for(int i=;i<query[x].size();i++)//与根节点有关的查询
{
if(vs[query[x][i]]) //如果查询的另一个节点已经访问过 则输出结果
printf("%d和%d的最近公共祖先为:%d\n",x,query[x][i],ancestor[findSet(query[x][i])]);
}
}
int main()
{
inputTree();//输入树
inputQuires();//输入查询
makeSet();
for(int i=;i<n;i++) ancestor[i]=i;
memset(vs,,sizeof(vs));
Tarjan(root);
return ;
}
LCA(最近公共祖先)的更多相关文章
- lca 最近公共祖先
http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...
- Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)
Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...
- LCA(最近公共祖先)模板
Tarjan版本 /* gyt Live up to every day */ #pragma comment(linker,"/STACK:1024000000,1024000000&qu ...
- CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )
CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...
- LCA近期公共祖先
LCA近期公共祖先 该分析转之:http://kmplayer.iteye.com/blog/604518 1,并查集+dfs 对整个树进行深度优先遍历.并在遍历的过程中不断地把一些眼下可能查询到的而 ...
- LCA 近期公共祖先 小结
LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...
- LCA最近公共祖先 ST+RMQ在线算法
对于一类题目,是一棵树或者森林,有多次查询,求2点间的距离,可以用LCA来解决. 这一类的问题有2中解决方法.第一种就是tarjan的离线算法,还有一中是基于ST算法的在线算法.复杂度都是O( ...
- Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】
一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...
- (转)Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)
基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个 ...
- LCA 最近公共祖先 tarjan离线 总结 结合3个例题
在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...
随机推荐
- javascript总结28 :匿名函数
1 匿名函数 //匿名函数. // (function (){ // console.log(1); // }) 2 匿名函数作用 //1.直接调用 (function (){ console.lo ...
- java程序员修炼之前笔记(前半部分)
第一部分 用java7做开发 第一章 初始java7 java7中的新特性 switch支持String 支持100_000_000数值表示法 新的异常处理 | 连接多个异常 final Except ...
- MATLAB搬移到别的电脑出现License Manager Error -9
是注册码的问题,不需要重装,主要是以前的安装包不见了.解决办法: 下一个KeyGen的MLMCrypt.exe文件.运行之后在当前目录下出现一个LICENSE.DAT文件. 复制到matlab.exe ...
- 编写高质量代码改善C#程序的157个建议——建议83:小心Parallel中的陷阱
建议83:小心Parallel中的陷阱 Parallel的For和ForEach方法还支持一些相对复杂的应用.在这些应用中,它允许我们在每个任务启动时执行一些初始化操作,在每个任务结束后,又执行一些后 ...
- sqpark编译时maven的pom文件内容(org.apachetype.maven.apachetypes:maven.archetypequickstat)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...
- 关于HTTP协议传输与接收数据的相关内容
第一篇: HTTP请求报文和HTTP响应报文 http://www.cnblogs.com/biyeymyhjob/archive/2012/07/28/2612910.html 第二篇: 深入浅出U ...
- [国家集训队] tree Ⅱ
bzoj2631(权限题...):链接 落咕:链接 考察的是LCT维护链上信息. 但是这个题不一样的是又有加法又有乘法...(有木有想到落咕的模板--线段树2qwq) 因为乘法的运算优先度比加法高,所 ...
- 279. 完全平方数 leetcode JAVA
题目: 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n.你需要让组成和的完全平方数的个数最少. 示例 1: 输入: n = 12 输出: 3 解释: ...
- 跑monkey前开启/关闭系统通知栏
@echo off cls title 别忘了跑monkey啊 :menu cls color 0A echo. .禁用systemui并重启 echo. .启用systemui并重启 echo. e ...
- 【loj#6503.】「雅礼集训 2018 Day4」Magic(生成函数+容斥)
题面 传送门 题解 复杂度比较迷啊-- 以下以\(n\)表示颜色总数,\(m\)表示总的卡牌数 严格\(k\)对比较难算,我们考虑容斥 首先有\(i\)对就代表整个序列被分成了\(m-i\)块互不相同 ...