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,商人从 ...
随机推荐
- leetcode979
搞不定这种递归计算,可能我的头脑是“线性”的,这种一层一层的,想起来太费劲了,想的头发都没了.以后希望能有AI来写这种程序吧,AI不怕掉头发! class Solution(object): def ...
- mvc中view与controll之间传递参数时,可以使用url进行传递
mvc中view与controller之间传递参数时,可以使用url进行传递,但是在url的地址中需要加上“id=123”这样的东西才行. 具体如代码: window.location.href = ...
- 分库工具mysql
sharding-sphere 比mycat 更方便 在于mycat属于中间件,更复杂,但也支持更多功能.查询功能更强.
- Modelsim command line 传参数到 .do 文件
gui跑mdelsim总觉得很麻烦,使用命令来启动方便了很多,类似linux一样,其实目前windows也可以做到,只是业界不怎么用windows罢了. 基于modelsim搭了一个UVM环境, 用 ...
- cdnbest里站点域名不同步到节点,报400错误的一般原因
报400错误一般是站点里的域名没有同步到节点上面的原因,产生的原因一般是下面两点原因: 1.检查节点列表如下图所示的状态是否打钩,这是节点和主控的通信状态,打叉表示连接有问题 这里打叉的几种原因(1) ...
- oracle用户间表数据复制迁移
如system用户要将scott中的emp表倒入其中,按如下方法: 1.登录scott用户 2.给system用户赋予查询emp标的权限: grant select on emp to system; ...
- day36 GIL锁与线程池
多进程与多线程效率对比 # # """ # # 计算密集型 # """ # from threading import Thread # f ...
- IBGP规划
在IBGP邻居中,使用反射器减少IBGP邻居关系是一种最常用的网络结构,在上图中AS65001中有6台路由器,其中,R1和R2是一组反射器,配置如下: R1: ! interface Loopback ...
- 41 【docker】初识
常用的docker命令: docker ps #查看当前正在运行的容器 docker ps -a | grep <keyword> #查看所有的容器,运行的或者停止的 docker sto ...
- python note 01 计算机基础与变量
1.计算机基础. 2.python历史. 宏观上:python2 与 python3 区别: python2 源码不标准,混乱,重复代码太多, python3 统一 标准,去除重复代码. 3.pyth ...