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,商人从 ...
随机推荐
- JEECG 上传插件升级-Online
前言: 现有的uploadify上传是基于swf的,随着H5的普及,flash即将退出历史舞台,JEECG本着与时俱进的原则,将全面升级JEECG系统中的上传功能,采用新式上传插件plupload,此 ...
- show出相应单据列表
var Fids=AddGroupItems.Select(o=>Convert.ToString(o["Id"])).ToArray(); string filter=st ...
- python的相对导入
最近断断续续学习flask,学到蓝本时候有点小问题卡住了,问题如下 导入包的时候py文件里使用了相对路径导入,但是这种导入方法不是很明白,就自己搜索加实验了终于有点眉目了 先定义一个包 adb包 这个 ...
- 学了 Python 能用来做这些!
来源商业新知网,原标题:学了 Python 能用来做什么? 说起编程语言,Python 也许不是使用最广的,但一定是现在被谈论最多的.随着近年大数据.人工智能的兴起,Python 越来越多的出现在人们 ...
- Linux学习-linux系统下安装jdk和tomcat,以及遇到的问题清单
安装JDK 1. 在usr目录下建立java安装目录 cd /usr mkdir java 2.下载jdk包 登录网址:http://www.oracle.com/technetwork/java ...
- Spring学习整理
Spring概述 将Spring理解为管理对象间依赖关系的容器 “解耦” 根据功能的不同,可以将一个系统中的代码分为 主业务逻辑 与 系统级业务逻辑 两类 ```Spring 是为了解决企业级开发的复 ...
- dagScheduler
由一个action动作触发sparkcontext的runjob,再由此触发dagScheduler.runJob,然后触发submitJob,封装一个JobSubmitted放入一个队列.然后再通过 ...
- CentOS7配置samba服务
Step1:安装samba相关软件 [root@node-1 ~]# yum -y install samba samba-client Step2:创建共享目录 [root@node-1 ~]# m ...
- canvas(二) lineCap demo
var dom = document.getElementById('clock'), ctx = dom.getContext('2d'); ctx.beginPath(); ctx.moveTo( ...
- hdpi对应分辨率
ldpi QVGA (240×320) mdpi HVGA (320×480) hdpi WVGA (480×800),FWVGA (480×854) xhdpi 720P(1280*720) ...