什么是最近公共祖先?

在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大公共祖先节点

换句话说,就是两个点在这棵树上距离最近的公共祖先节点

所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径。

常用来求LCA的算法有:Tarjan/DFS(离线),ST/倍增(在线)。

1,Tarjan

tarjan的算法复杂度为$O(n+q)$。

思路:每进入一个节点u的深搜,就把整个树的一部分看作以节点u为根节点的小树,再搜索其他的节点。每搜索完一个点后,如果该点和另一个已搜索完点为需要查询LCA的点,则这两点的LCA为另一个点的现在的祖先。

  1. 先建两个图,一个为树的各条边,另一个是需要查询最近公共祖先的两节点。
  2. 建好后,从根节点开始进行一遍深搜。
  3. 先把该节点u的father设为他自己(也就是只看大树的一部分,把那一部分看作是一棵树),搜索与此节点相连的所有点v,如果点v没被搜索过,则进入点v的深搜,深搜完后把点v的father设为点u。
  4. 深搜完一点u后,开始判断节点u与另一节点v是否满足求LCA的条件,满足则将结果存入数组中。
  5. 搜索完所有点,自动退出初始的第一个深搜,输出结果。

例.poj1470

传送:http://poj.org/problem?id=1470

题意:有n个点的树。m个询问,每个询问要求求出<u,v>的LCA。最终输出某个点作为询问中的LCA出现的次数。

分析:(题意描述有点不清喵喵喵???默认输入是父亲到儿子,然后手动记录入度,找到根节点。

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
const int maxn=;
typedef pair<int,int> pii;
vector<pii> q[maxn];
vector<int> mp[maxn];
int ans[maxn*maxn],num[maxn*maxn];
int f[maxn],vis[maxn],flag[maxn];
int find(int x){
if (f[x]==x) return x;
else return f[x]=find(f[x]);
}
void LCA(int root){
f[root]=root;
vis[root]=;
for (int i=;i<mp[root].size();i++){
int tmp=mp[root][i];
if (vis[tmp]) continue;
LCA(tmp);
f[tmp]=root;
}
for (int i=;i<q[root].size();i++){
pii tmp=q[root][i];
if (vis[tmp.first])
ans[tmp.second]=find(tmp.first);
}
}
int main(){
int n,m,x,y,kk;
while (~scanf("%d",&n)){
memset(flag,,sizeof(flag));
for (int i=;i<n;i++){
scanf("%d:(%d)",&x,&kk);
while (kk--){
scanf("%d",&y);
flag[y]=;
mp[x].push_back(y);
//mp[y].push_back(x);
}
}
scanf("%d",&m); char ch;
for (int i=;i<m;i++){
cin >> ch;
scanf("%d %d)",&x,&y);
q[x].push_back({y,i});
q[y].push_back({x,i});
}
for (int i=;i<=n;i++) vis[i]=;
int root;
for (int i=;i<=n;i++) if (!flag[i]){root=i;break;}
LCA(root);
memset(num,,sizeof(num));
for (int i=;i<m;i++) num[ans[i]]++;
for (int i=;i<=n;i++)
if (num[i]) printf("%d:%d\n",i,num[i]);
}
return ;
}

poj1470

练习:

1.hdu2586 How far away ?

传送:http://acm.hdu.edu.cn/showproblem.php?pid=2586

题意:求树上两点的最短距离。

分析:LCA。

树上最短路:$ans=dist[u]+dist[v]-2*dist[LCA(u,v)]$。

 #include<bits/stdc++.h>
using namespace std;
const int maxn=4e4+;
typedef pair<int,int> pii;
struct node{
int to,val;
};
vector<node> mp[maxn];
vector<pii> q[maxn];
int ans[],f[maxn],vis[maxn],dis[maxn],flag[maxn];
int find(int x){
if (f[x]==x) return x;
else return f[x]=find(f[x]);
}
void LCA(int root){
f[root]=root;
vis[root]=;
for (int i=;i<mp[root].size();i++){
node tmp=mp[root][i];
if (vis[tmp.to]) continue;
dis[tmp.to]=dis[root]+tmp.val;
LCA(tmp.to);
f[tmp.to]=root;
}
for (int i=;i<q[root].size();i++){
pii tmp=q[root][i];
if (vis[tmp.first]){
ans[tmp.second]=dis[root]+dis[tmp.first]-*dis[find(tmp.first)];
}
}
}
int main(){
int t,n,m,x,y,z; scanf("%d",&t);
while (t--){
scanf("%d%d",&n,&m);
for (int i=;i<=n;i++) mp[i].clear(),flag[i]=,q[i].clear(),dis[i]=,vis[i]=;
for (int i=;i<n-;i++){
scanf("%d%d%d",&x,&y,&z);
mp[x].push_back({y,z});
mp[y].push_back({x,z});
flag[y]=;
}
for (int i=;i<m;i++){
scanf("%d%d",&x,&y);
q[x].push_back({y,i});
q[y].push_back({x,i});
}
int root;
for (int i=;i<=n;i++) if (!flag[i]){root=i;break;}
LCA(root);
for (int i=;i<m;i++) printf("%d\n",ans[i]);
}
return ;
}

hdoj2586

2.hdu2874 Connections between cities

传送:http://acm.hdu.edu.cn/showproblem.php?pid=2874

题意:n个点,m条边。问树上两点间最短路。

分析:图是森林。LCA。(卡内存,mle了若干发。

需要判断未经过的点作为新的一个树,求解答案。

 #include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+;
const int maxm=1e6+;
typedef pair<int,int> pii;
struct node{
int to,val,nxt;
}mp[maxm*];
struct node2{
int to,id,nxt;
}q[maxm*];
int head[maxn],q_head[maxn];
int ans[maxm],f[maxn],dis[maxn];
bool vis[maxn];
int tot,tot2;
void addedge(int x,int y,int z){
mp[tot].to=y;
mp[tot].val=z;
mp[tot].nxt=head[x];
head[x]=tot++;
}
void addquery(int x,int y,int id){
q[tot2].to=y;
q[tot2].id=id;
q[tot2].nxt=q_head[x];
q_head[x]=tot2++;
}
int find(int x){
if (f[x]==x) return x;
else return f[x]=find(f[x]);
}
void LCA(int root){
f[root]=root;
vis[root]=;
for (int i=head[root];i!=-;i=mp[i].nxt){
node tmp=mp[i];
if (vis[tmp.to]) continue;
dis[tmp.to]=dis[root]+tmp.val;
LCA(tmp.to);
f[tmp.to]=root;
}
for (int i=q_head[root];i!=-;i=q[i].nxt){
node2 tmp=q[i];
if (vis[tmp.to]){
if (dis[tmp.to]!=-) ans[tmp.id]=dis[root]+dis[tmp.to]-*dis[find(tmp.to)];
}
}
}
int main(){
int n,m,c,x,y,z;
while (~scanf("%d%d%d",&n,&m,&c)){
//for (int i=1;i<=n;i++) head[i]=-1,q_head[i]=-1,flag[i]=false;
//for (int i=0;i<=c;i++) ans[i]=-1;
memset(head,-,sizeof(head));
memset(q_head,-,sizeof(q_head));
memset(ans,-,sizeof(ans));
memset(vis,,sizeof(vis));
tot=;tot2=;
for (int i=;i<m;i++){
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
addedge(y,x,z);
}
for (int i=;i<c;i++){
scanf("%d%d",&x,&y);
addquery(x,y,i);
addquery(y,x,i);
}
for (int i=;i<=n;i++){
if (!vis[i]){
//for (int j=1;j<=n;j++) vis[j]=0,dis[j]=0;
memset(dis,-,sizeof(dis));
dis[i]=;
LCA(i);
}
}
for (int i=;i<c;i++){
if (ans[i]==-) printf("Not connected\n");
else printf("%d\n",ans[i]);
}
}
return ;
}

hdoj2874

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

  1. LCA 最近公共祖先 tarjan离线 总结 结合3个例题

    在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...

  2. HDU 4547 CD操作 (LCA最近公共祖先Tarjan模版)

    CD操作 倍增法  https://i.cnblogs.com/EditPosts.aspx?postid=8605845 Time Limit : 10000/5000ms (Java/Other) ...

  3. LCA最近公共祖先——Tarjan模板

    LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. Tarjan是一种离线算法,时间复杂度O(n+Q),Q表示询问次数,其中 ...

  4. LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现

    首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵 ...

  5. LCA最近公共祖先 Tarjan离线算法

    学习博客:  http://noalgo.info/476.html 讲的很清楚! 对于一颗树,dfs遍历时,先向下遍历,并且用并查集维护当前节点和父节点的集合.这样如果关于当前节点(A)的关联节点( ...

  6. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)

    Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...

  7. LCA(最近公共祖先)模板

    Tarjan版本 /* gyt Live up to every day */ #pragma comment(linker,"/STACK:1024000000,1024000000&qu ...

  8. LCA 近期公共祖先 小结

    LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...

  9. lca 最近公共祖先

    http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...

  10. CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )

    CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...

随机推荐

  1. CSS vertical-align属性详解

    . 首页 博客园 联系我 前言:关于vertical-align属性. 实践出真知. 垂直居中. 第二种用法. 留言评论 返回顶部 前言:关于vertical-align属性 vertical-ali ...

  2. RocketMQ入门(Filter)_5

    RocketMQ中存储的消息对于消费者来说,并不完全都是他们需要的,因此需要对消息进行过滤. 订阅Topic主题 ,选择Tags都是我们简单的过滤.Topic是大分类,Tags是二级分类. Rocke ...

  3. js 模拟css3 动画1

    <html> <head> <title> javaScript缓动入门 </title> </head> <body> < ...

  4. 保存xml报错 'UTF_8' is not a supported encoding name

    ArgumentException: 'UTF_8' is not a supported encoding name. For information on defining a custom en ...

  5. PostgreSQL使用笔记

    下载并安装 注意安装图形界面 pgAdmin 需要输入缺省用户 postgres 的密码 在 Windows 下安装之后注意把 bin文件夹加到 Path 环境变量中. 重置密码 使用管理员权限打开 ...

  6. pycharm的断点调试【转自https://blog.csdn.net/weixin_39198406/article/details/78873120】

    1. show execution point (F10)显示目前项目所有断点2. step over (F8)下一步但仅限于设置断点的文件3. step into (F7)执行下一行4. step ...

  7. rest_framework常用设置

    1.常用配置 import django_filters from django.db.models import Q from rest_framework.pagination import Pa ...

  8. chrome不能浏览任何网页,提示配置proxy,Ubuntu

    自从在Ubuntu安装virtualbox以后,我的chrome浏览器就不能上网了,提示我检查proxy信息, 后面设置了noproxy就ok啦. 不用使用命令,一次设置,终身有效. 首先,安装gks ...

  9. 相机标定——OpenCV-Python Tutorials

    原文地址http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py ...

  10. 254. Factor Combinations 返回所有因数组合

    [抄题]: Numbers can be regarded as product of its factors. For example, 8 = 2 x 2 x 2; = 2 x 4. Write ...