LCA(最近公共祖先)——Tarjan
什么是最近公共祖先?
在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点。
换句话说,就是两个点在这棵树上距离最近的公共祖先节点。
所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径。
常用来求LCA的算法有:Tarjan/DFS(离线),ST/倍增(在线)。
1,Tarjan
tarjan的算法复杂度为$O(n+q)$。
思路:每进入一个节点u的深搜,就把整个树的一部分看作以节点u为根节点的小树,再搜索其他的节点。每搜索完一个点后,如果该点和另一个已搜索完点为需要查询LCA的点,则这两点的LCA为另一个点的现在的祖先。
- 先建两个图,一个为树的各条边,另一个是需要查询最近公共祖先的两节点。
- 建好后,从根节点开始进行一遍深搜。
- 先把该节点u的father设为他自己(也就是只看大树的一部分,把那一部分看作是一棵树),搜索与此节点相连的所有点v,如果点v没被搜索过,则进入点v的深搜,深搜完后把点v的father设为点u。
- 深搜完一点u后,开始判断节点u与另一节点v是否满足求LCA的条件,满足则将结果存入数组中。
- 搜索完所有点,自动退出初始的第一个深搜,输出结果。
例.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的更多相关文章
- LCA 最近公共祖先 tarjan离线 总结 结合3个例题
在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...
- HDU 4547 CD操作 (LCA最近公共祖先Tarjan模版)
CD操作 倍增法 https://i.cnblogs.com/EditPosts.aspx?postid=8605845 Time Limit : 10000/5000ms (Java/Other) ...
- LCA最近公共祖先——Tarjan模板
LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. Tarjan是一种离线算法,时间复杂度O(n+Q),Q表示询问次数,其中 ...
- LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现
首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵 ...
- LCA最近公共祖先 Tarjan离线算法
学习博客: http://noalgo.info/476.html 讲的很清楚! 对于一颗树,dfs遍历时,先向下遍历,并且用并查集维护当前节点和父节点的集合.这样如果关于当前节点(A)的关联节点( ...
- 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 ...
- LCA 近期公共祖先 小结
LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...
- lca 最近公共祖先
http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...
- CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )
CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...
随机推荐
- CSS vertical-align属性详解
. 首页 博客园 联系我 前言:关于vertical-align属性. 实践出真知. 垂直居中. 第二种用法. 留言评论 返回顶部 前言:关于vertical-align属性 vertical-ali ...
- RocketMQ入门(Filter)_5
RocketMQ中存储的消息对于消费者来说,并不完全都是他们需要的,因此需要对消息进行过滤. 订阅Topic主题 ,选择Tags都是我们简单的过滤.Topic是大分类,Tags是二级分类. Rocke ...
- js 模拟css3 动画1
<html> <head> <title> javaScript缓动入门 </title> </head> <body> < ...
- 保存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 ...
- PostgreSQL使用笔记
下载并安装 注意安装图形界面 pgAdmin 需要输入缺省用户 postgres 的密码 在 Windows 下安装之后注意把 bin文件夹加到 Path 环境变量中. 重置密码 使用管理员权限打开 ...
- 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 ...
- rest_framework常用设置
1.常用配置 import django_filters from django.db.models import Q from rest_framework.pagination import Pa ...
- chrome不能浏览任何网页,提示配置proxy,Ubuntu
自从在Ubuntu安装virtualbox以后,我的chrome浏览器就不能上网了,提示我检查proxy信息, 后面设置了noproxy就ok啦. 不用使用命令,一次设置,终身有效. 首先,安装gks ...
- 相机标定——OpenCV-Python Tutorials
原文地址http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py ...
- 254. Factor Combinations 返回所有因数组合
[抄题]: Numbers can be regarded as product of its factors. For example, 8 = 2 x 2 x 2; = 2 x 4. Write ...