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,商人从 ...
随机推荐
- json介绍和使用
最近在开发时需要用到json,所以在各种寻找json相关的博客,恰巧在博客园里就有一篇写的很不错的,在这里推荐下:http://www.cnblogs.com/Truly/archive/2006/1 ...
- SpringMVC Get请求传集合,前端"异步"下载excel 附SpringMVC 后台接受集合
最近项目上管理后台需要自己做一部分js部分,之前都是前端来弄...碰到了下载excel,刚开始使用ajax,搞了好久发现不合适..下载不了,网上说ajax返回类型不支持二进制流.. 因此采用 wind ...
- ==、equals和hashCode小结
1.== ==是关系操作符,对于基本类型(byte,short,char,int,long,float,double,boolean),比较的是值是否相等:对于对象,比较的是对象的引用(也即栈内存中的 ...
- go相关知识点
后续开发go相关, 环境搭建 go env //查看环境所有 go只有三种引用类型 slice(切片). map(字典). channel(管道): go的类型的浅记忆 4仲类型bool,字符串,数字 ...
- week07 13.2 NewsPipeline之 二 News Fetcher - Xpath
我们使用Xpath来专门做一个scrapter 我们专门弄个文件夹 里面全部是 各个新闻源(CNN BBC等)的scraper来抓取网站的text内容 主要函数(就是传入text内容的那个url)然后 ...
- 通过windows远程桌面连接CentOS系统
前提: CentOS安装桌面,如果无桌面,请执行 # yum -y groups install "GNOME Desktop" # startx 1 2 配置源 # yum in ...
- java学习笔记(十一):重写(Override)与重载(Overload)
重写(Override) 重写是子类对父类的允许访问的方法的进行重新编写, 但是返回值和形参都不能改变. 实例 class Animal{ public void run(){ System.out. ...
- Pandas聚合
数据聚合 import pandas as pd from pandas import Series import numpy as np # 准备数据 df = pd.DataFrame([[-0. ...
- php RSA非对称加密 的实现
基本概念 加密的意义 加密的意义在于数据的传输过程中,即使被第三方获取到传输的数据,第三方也不能获取到数据的具体含义. 加密方式分为对称加密和非对称加密 什么是对称加密? 对称加密只使用一个秘钥,加密 ...
- 365. Water and Jug Problem量杯灌水问题
[抄题]: 简而言之:只能对 杯子中全部的水/容量-杯子中全部的水进行操作 You are given two jugs with capacities x and y litres. There i ...