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(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...
随机推荐
-
【原创】开源Math.NET基础数学类库使用(15)C#计算矩阵行列式
本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新 开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 上个月 ...
-
ural 1341. Device
1341. Device Time limit: 1.0 secondMemory limit: 64 MB Major (M): You claimed that your device would ...
-
简明易懂的call apply
在iteye看到一篇对call解释得相当简明易懂,觉得得宣传一下 : http://uule.iteye.com/blog/1158829 一.方法的定义 call方法: 语法:call([thisO ...
-
oc 字符串
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { ...
-
[主席树]SPOJ DQUERY
题目链接 题意:n个数 m个查询 查询的是[l, r]区间内不相同的数的个数 没有修改,因此静态的主席树就好了 将重复的元素建树即可 query的时候加起来,用区间长度(r-l+1)去减就是答案 (q ...
-
css3种方法实现元素的绝对居中
元素的绝对居中应该是很多人熟悉的一个小应用,我记得很多年前去神州数码面试的时候就遇到过这个面试题.方法比较简单,代码如下: .node{ width : 300px; height : 400px; ...
-
JS基础DOM篇之一:何为DOM?
近日在园子看了一篇文章,一位前端负责人问应聘者何为DOM事件流的三个阶段,我当时一看也是懵圈,于是强迫症复发,遂想要搞清楚它.谁知在查资料的过程中发现有好多关于DOM的概念也是模糊不清,便决定继续延伸 ...
-
python中的三种输入方式
python中的三种输入方式 python2.X python2.x中以下三个函数都支持: raw_input() input() sys.stdin.readline() raw_input( )将 ...
-
linux系统调用之网络管理2
socketcall socket系统调用 socket 建立socket bind 绑定socket到端口 connect 连接远程主机 accept 响应socket连接请求 send 通过soc ...
-
python中time模块常用功能
import time time模块提供了大量对时间进行处理的方法 time.time() # 获取当前时间戳,得到自1970年开始的秒数 >>>time.time() 155487 ...