最近公共祖先

定义:给定一颗有根树,若结点 z 既是 x 的祖先,也是 y 的祖先,则称 z 是 x,y 的公共祖先。在 x,y 所有的公共祖先中,深度最大的一个称为 x,y 的最近公共祖先,简称\(LCA(x,y)\)。

求解最近公共祖先一般有三种解法:向上标记法,树上倍增法和 Tarjan 算法。

1.向上标记法

即对于任何两个结点 x , y ,分别从x y 向上走并标记它们所有经过的节点,第一次相遇的节点即为最近公共祖先。其时间复杂度最坏为\(O(n^2)\)。

2.树上倍增法

树上倍增法在很多问题都有很广泛的应用,当然最近公共祖先也可以用树上倍增算法来求,其基本思想是:

设d[x]>=d[y],其中d[x]表示x的深度,然后利用二进制思想,将x不断调整直至和y在同一深度,检查此时x和y是否相等,若相等即为最近公共祖先,否则将x,y一起向上调整直至相等,此时则为最近公共祖先。

该算法时间复杂度为:\(O((n+m)logn)\)具体代码(裸题,求权值)为:

#include<bits/stdc++.h>

using namespace std;
const int maxn=5e5+10;
int head[maxn],dis[maxn],d[maxn],f[maxn][20];
struct Edge
{
int nex,to,val;
}edge[maxn<<1];
int T,t,n,m,tot;
queue<int> q;
void init()
{
tot=0;
memset(head,-1,sizeof(head));
memset(d,0,sizeof(d));
}
void add(int from,int to,int val)
{
edge[++tot].to=to;
edge[tot].val=val;
edge[tot].nex=head[from];
head[from]=tot;
}
void bfs()
{
q.push(1);
d[1]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i!=-1;i=edge[i].nex)
{
int y=edge[i].to;
if(d[y]) continue;
d[y]=d[x]+1;
dis[y]=dis[x]+edge[i].val;
f[y][0]=x;
for(int j=1;j<=t;++j)
f[y][j]=f[f[y][j-1]][j-1];
q.push(y);
}
}
}
int lca(int x,int y)
{
if(d[x]<d[y]) swap(x,y);
for(int i=t;i>=0;--i)
if(d[f[x][i]]>=d[y]) x=f[x][i];
if(x==y) return x;
for(int i=t;i>=0;--i){
if(f[x][i]!=f[y][i]){
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
} int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
t=(int)(log(n)/log(2))+1;
init();
for(int i=1;i<n;++i){
int a,b,val;
scanf("%d %d %d",&a,&b,&val);
add(a,b,val);
add(b,a,val);
}
bfs();
for(int i=1;i<=m;++i){
int x,y;
scanf("%d %d",&x,&y);
printf("%d\n",dis[x]+dis[y]-2*dis[lca(x,y)]);
}
}
}

3.Tarjan算法

Tarjan算法本质上是用并查集对“向上标记法”的优化。它是一个离线算法(即将所有输入都输入完成后一起输出);其时间复杂度为:\(O(n+m)\)。

该算法将树中所有结点分成了三类:访问并回溯过,访问但为回溯过,未访问三类。该三类点分别标记为2,1和0。该算法的思想是当一个结点被标记为2时,则将它所在集合合并到它的父节点所在的集合中,这就相当于每个完成回溯的结点都有一个指针指向其父节点,所以只需查询y所在集合的代表元素,就等价与从y一直向上走到一个开始递归但尚未回溯的结点,即\(LCA(x,y)\)。

还是上面的题面,看代码:

#include<bits/stdc++.h>

using namespace std;
const int maxn=5e4+10;
const int maxe=210;
const int inf=0x3f3f3f3f;
struct Edge
{
int nex,to,val;
}edge[maxn<<1];
int T,n,m,t,tot;
int head[maxn],fa[maxn],v[maxn],dis[maxn],ans[maxe];
vector<int> query[maxn],query_id[maxn];
void init()
{
tot=0;
memset(head,-1,sizeof(head));
memset(dis,0,sizeof(dis));
memset(v,0,sizeof(v));
for(int i=1;i<=n;++i){
fa[i]=i;
query[i].clear();
query_id[i].clear();
}
}
void add(int from,int to,int val)
{
edge[++tot].to=to;
edge[tot].val=val;
edge[tot].nex=head[from];
head[from]=tot;
}
void add_query(int x,int y,int id)
{
query[x].push_back(y);
query_id[x].push_back(id);
query[y].push_back(x);
query_id[y].push_back(id);
}
int ffind(int x)
{
return x==fa[x]?x:fa[x]=ffind(fa[x]);
}
void tarjan(int x)
{
v[x]=1;
for(int i=head[x];i!=-1;i=edge[i].nex)
{
int y=edge[i].to;
if(v[y]) continue;
dis[y]=dis[x]+edge[i].val;
tarjan(y);
fa[y]=x;
}
for(int i=0;i<query[x].size();++i)
{
int y=query[x][i],id=query_id[x][i];
if(v[y]==2){
int lca=ffind(y);
ans[id]=min(ans[id],dis[x]+dis[y]-2*dis[lca]);
}
}
v[x]=2;
} int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
init();
for(int i=1;i<n;++i){
int a,b,val;
scanf("%d %d %d",&a,&b,&val);
add(a,b,val);
add(b,a,val);
}
for(int i=1;i<=m;++i){
int x,y;
scanf("%d %d",&x,&y);
if(x==y) ans[i]=0;
else{
add_query(x,y,i);
ans[i]=inf;
}
}
tarjan(1);
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
}
system("pause");
}

最近公共祖先(LCA)问题的更多相关文章

  1. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)

    Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...

  2. POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)

    POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...

  3. POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)

    POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...

  4. [模板] 最近公共祖先/lca

    简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...

  5. 【lhyaaa】最近公共祖先LCA——倍增!!!

    高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...

  6. POJ 1470 Closest Common Ancestors (最近公共祖先LCA 的离线算法Tarjan)

    Tarjan算法的详细介绍,请戳: http://www.cnblogs.com/chenxiwenruo/p/3529533.html #include <iostream> #incl ...

  7. 【Leetcode】查找二叉树中任意结点的最近公共祖先(LCA问题)

    寻找最近公共祖先,示例如下: 1 /           \ 2           3 /    \        /    \ 4    5      6    7 /    \          ...

  8. 最近公共祖先LCA(Tarjan算法)的思考和算法实现

    LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...

  9. 查找最近公共祖先(LCA)

    一.问题 求有根树的任意两个节点的最近公共祖先(一般来说都是指二叉树).最近公共祖先简称LCA(Lowest Common Ancestor).例如,如下图一棵普通的二叉树. 结点3和结点4的最近公共 ...

  10. 最近公共祖先(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- ...

随机推荐

  1. python接口自动化测试 - unittest框架基本使用

    unittest简单介绍 单元测试框架 还可以适用WEB自动化测试用例的开发与执行 提供丰富的断言方法 官方文档:https://docs.python.org/zh-cn/3/library/uni ...

  2. C++模板详解(三):参数化声明详解

    在前两节中(C++模板详解(一).C++模板详解(二)),我们了解了函数模板和类模板的基本概念和使用方法.在这篇博文里,我们主要来详细地阐述一下"模板的参数声明"这个话题,并且也谈 ...

  3. linux下安装mongo数据库存

    https://www.runoob.com/mongodb/mongodb-linux-install.html 安装完后,要重启一下,否则无法运行./mongod 下载完安装包,并解压 tgz(以 ...

  4. LAMP源码编译

    ---恢复内容开始--- 1.LAMP源码编译的基础环境 安装组建包:yum groupinstall "Development Tools" "Development ...

  5. wordpress 代码获得菜单的方法

    1.第一种,在function.php中添加这个东东,wp_nav_menu()主要是用这个函数 $menu = 'menu'; $nav_menu = wp_get_nav_menu_object( ...

  6. js判断是横屏还是竖屏

    1通过在html中分别引用横屏和竖屏的样式: <link rel="stylesheet" media="all and (orientation:portrait ...

  7. Linux编程日常错误

    编译的时候出现如下错误提示: undefined reference to `sem_init'undefined reference to `sem_post'undefined reference ...

  8. MySQL导出数据到文件报错

    执行如下语句: mysql> select * from users into outfile "F:\Develop\MySQL57\Uploads\users.txt" ...

  9. iOS 开发之函数式编程思想(Functional Programming)

    函数式编程(Functional Programming), 函数式编程强调的函数:1.不依赖外部状态:2.不改变外部状态. 函数式编程可解决线程安全问题,每一个函数都是线程安全的. 时间状态:变量一 ...

  10. Docker安装、命令详情、层级架构、docker服务启动失败解决方法

    容器背景: 层级架构:  容器对比传统化虚拟机: 可以把docker理解成是一款自带软件(比如:nignx.tomcat.....)的镜像操作系统(首先是要下载镜像) 以下是Windows环境安装Do ...