LCA(最近公共祖先)--tarjan离线算法 hdu 2586
HDU 2586 How far away ?
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 11320 Accepted Submission(s):
4119
bidirectional roads connecting them. Every day peole always like to ask like
this "How far is it if I want to go from house A to house B"? Usually it hard to
answer. But luckily int this village the answer is always unique, since the
roads are built in the way that there is a unique simple path("simple" means you
can't visit a place twice) between every two houses. Yout task is to answer all
these curious people.
the number of test cases.
For each test case,in the first line there are
two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses
and the number of queries. The following n-1 lines each consisting three numbers
i,j,k, separated bu a single space, meaning that there is a road connecting
house i and house j,with length k(0<k<=40000).The houses are labeled from
1 to n.
Next m lines each has distinct integers i and j, you areato answer
the distance between house i and house j.
the answer of the query. Output a bland line after each test case.
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1
25
100
100
2009 Spring Contest
概念描述:
LCA(Least Common Ancestors):即最近公共祖先,是指这样一个问题:在有根树中,找出某两个结点u和v的所有祖先中距离(u,v)最近的那个公共祖先(也就是离根最远的那个公共祖先)。
时间和空间复杂度:
- 时间复杂度:当询问次数为Q,节点数为N时,时间复杂度为O(N+Q)。
- 空间复杂度:①建图时存储的空间大小(树的节点个数个空间);②深搜时遍历树时需要的空间大小(树的最大深度)
算法实现基于基本原理:
算法是基于DFS和并查集来实现的。
算法流程 及 对求LCA(u,v)的证明:
- 从根节点(root)开始深搜,
- 对于新搜索到的一个节点(x),首先创建由这个结点构成的集合(由par数组维护,par[x]=x,当前这个集合中只有元素x),
- 然后依次搜索并处理该节点包含的所有子树(搜素和处理是个递归的过程。结合第5点理解:每搜索完一棵子树,则可确定子树内的LCA询问都已解,其他的LCA询问的结果必然在这个子树之外。)
- 当搜索完该节点的所有子树以后,在回溯时,把当前节点的par[x]=(x的父亲节点)(集合归并)
- 然后开始处理原先所有询问中包含了该节点的所有询问及求LCA(u,?)(体现了离线算法,对询问次序按深搜时遍历到的节点顺序进行重组)
- 在处理包含该节点的询问中,先判断当前正在处理的这条询问(求LCA(u,v))中另一个节点(v)是否也已经被遍历过,
- 若还未遍历,则暂不处理;否则LCA(u,v)= FindPar(par[v])(并查集)。(证明:因为v是在遍历到u(也就是当前的x节点)之前先遍历到了。如果有一个从当前结点(u)到结点v的询问,且v已被检查过,则由于进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v所在集合的祖先。)
- 包含该节点的所有询问全部处理完毕
<具体结合遍历和并查集的归并的顺序理解,见图>

处理LCA(3,4):因为3在遍历到4之前先被访问到,所以LCA(3,4)=FindPar(par[3])=3;(此时对LCA(4,5)的询问操作暂被跳过。
处理LCA(5,4):因为4在遍历到5之被访问,所以LCA(5,4)=Find(par[4])=2
扩展:求树上两点(u,v)间的最短距离

求树上两点间u,v的最短距离的方法:记dis[u]为u到根节点的距离
那么u到v之间的距离:ans[u][v]=dis[u]+dis[v]-2*dis[lca[u][v]](减去到根节点的公共距离的两倍);
题解:
#include<cstring>
#include<iostream>
using namespace std;
#include<cstdio>
#define M 201
#include<vector>
#define N 40100
vector <int> que[N];/*储存询问队列*/
int ans[M];/*存着答案*/
int n,m,u,v,k;
struct Edge{
int v,last,w;/*边表*/
}edge[N*];
int head[N],dis[N]={};
int T,t=;
int father[N],ance[N];/*father[N]表示当前的处理的子树,ance代表这个点当前的祖先*/
bool visit[N],root[N];/*visit判断当前的点的子树lca是否求过,root是寻找根节点*/
void add_edge(int u,int v,int w)
{
++t;
edge[t].v=v;
edge[t].w=w;
edge[t].last=head[u];
head[u]=t;
}
void input()
{
memset(visit,false,sizeof(visit));
memset(root,false,sizeof(root));
memset(head,,sizeof(head));
memset(dis,,sizeof(dis));
memset(edge,,sizeof(edge));
scanf("%d%d",&n,&m);
for(int i=;i<=n-;++i)
{
scanf("%d%d%d",&u,&v,&k);
add_edge(u,v,k);
root[v]=true;
ance[i]=i;/*初始化*/
father[i]=i;
}
for(int i=;i<=m;++i)
{
scanf("%d%d",&u,&v);
que[u].push_back(i);/*因为离线算法求出结果是无法知道他的查询顺序的,但是我们要按照查询顺序输出,所以就在查询序列的偶数位存着下一位的在ans的顺序*/
que[u].push_back(v);
que[v].push_back(i);
que[v].push_back(u);
}
}
int find(int x)
{
return (father[x]==x)?father[x]:father[x]=find(father[x]);
}
void tarjan(int k,int w)
{
ance[k]=k;
dis[k]=w;
for(int l=head[k];l;l=edge[l].last)
{
tarjan(edge[l].v,dis[k]+edge[l].w);
father[edge[l].v]=k;/*father存着当前点与全部的子树上的集合,同时把子树直接点的祖先设为k,所以查询一个子树上的点的区间的时候,要先用find找出代表元素,再求祖先*/
ance[edge[l].v]=k;
}
visit[k]=true;
int size=que[k].size();
for(int i=;i<size;i+=)
{
if(visit[que[k][i]])/*如果这条边的另一个点的lca已经求出来,那么这个点所在集合的祖先就是这两个点的最近公共祖先*/
{
int zu=ance[find(que[k][i])];
ans[que[k][i-]]=dis[k]+dis[que[k][i]]-*dis[zu];
}
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
input();
for(int i=;i<=n;++i)
{
if(!root[i])/*从根节点开始深搜*/
{
tarjan(i,);
break;
}
}
for(int i=;i<=m;++i)
printf("%d\n",ans[i]);
}
return ;
}
LCA(最近公共祖先)--tarjan离线算法 hdu 2586的更多相关文章
- LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现
首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵 ...
- LCA最近公共祖先 Tarjan离线算法
学习博客: http://noalgo.info/476.html 讲的很清楚! 对于一颗树,dfs遍历时,先向下遍历,并且用并查集维护当前节点和父节点的集合.这样如果关于当前节点(A)的关联节点( ...
- LCA 最近公共祖先 tarjan离线 总结 结合3个例题
在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...
- LCA问题的ST,tarjan离线算法解法
一 ST算法与LCA 介绍 第一次算法笔记这样的东西,以前学算法只是笔上画画写写,理解了下,刷几道题,其实都没深入理解,以后遇到新的算法要把自己的理解想法写下来,方便日后回顾嘛>=< R ...
- LCA(最近公共祖先)离线算法Tarjan+并查集
本文来自:http://www.cnblogs.com/Findxiaoxun/p/3428516.html 写得很好,一看就懂了. 在这里就复制了一份. LCA问题: 给出一棵有根树T,对于任意两个 ...
- 求LCA最近公共祖先的离线Tarjan算法_C++
这个Tarjan算法是求LCA的算法,不是那个强连通图的 它是 离线 算法,时间复杂度是 O(m+n),m 是询问数,n 是节点数 它的优点是比在线算法好写很多 不过有些题目是强制在线的,此类离线算法 ...
- 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算法利用并查集在一次DFS(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...
随机推荐
- Linux 下解决安装多个node冲突的问题(重新安装node)
一个系统中不经意安装了多个node版本,结果更新后还是原来的版本,下面思考一下解决办法: 敲黑板: 1. nodejs 用 包管理器安装一般在 /usr/local/bin 2. 查看当前目录下的no ...
- 一键切图 PS 动作 【收藏】
使用方法 一键切图动作.zip 1. 下载动作 2. 打开PS 动作 窗口,导入动作 3. 选中图层后 点击 F2 一键切图 详情看原文链接 原文链接
- Mysql储存过程1: 设置结束符与储存过程创建
#显示储存过程 show procedure status; #设置结束符 delimiter $; #创建储存过程 create procedure procedure_name() begin - ...
- Linux 查看网卡流量【转】
我的系统式RHEL5. 在linux下,查看网卡流量的方法有很多.下面先记录几个,和他们的大概用法.已被以后之需. 一:iptraf 一个很不错的工具.RHEL5 iso自带有,我 ...
- openjudge-NOI 2.6-1775 采药
题目链接:http://noi.openjudge.cn/ch0206/1775/ 题解: 很经典的01背包问题,设时间为t,价值为v 一维压缩,状态转移方程fj=max(fj,fj-ti+vi) # ...
- selenium grid结构图
调用 Selenium-Grid 的基本结构图如下: 上面是使用 selenium-grid 的一种普通方式,仅仅使用了其支持的分布式执行的功能,即当你同时需 要测试用例比较多时,可以平行的执行这些用 ...
- centos上git搭建
1 git的安装需要一些包: yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-Ex ...
- 《跟老齐学Python Django实战》读后感
1.说一下这本书,讲解的很细致,内容选取足够入门Django. 2.在学习这本书要注意的几点: <1>如果你想跟着敲这本书的代码必须要安装:Django版本1.10.1(当然也可以玩玩新版 ...
- caffe使用finetume
训练时, solver.prototxt中使用的是train_val.prototxt ./build/tools/caffe/train -solver ./models/bvlc_referenc ...
- 连接数据库:ERROR:The server time zone value '?й???????' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration prop
本打算在maven项目中配置mybatis试试看,想到mybatis如果不是在容器中运行,那么他的事务控制实际上可以使用的是jdbc的提交和回滚,这就要在pom.xml文件中配置mysql-conne ...