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

Problem Description
 
There are n houses in the village and some
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.
 
Input
First line is a single integer T(T<=10), indicating
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.
 
Output
For each test case,output m lines. Each line represents
the answer of the query. Output a bland line after each test case.
 
Sample Input
2
3 2
1 2 10
3 1 15
1 2
2 3

2 2
1 2 100
1 2
2 1

 
Sample Output
10
25
100
100
 
Source
ECJTU
2009 Spring Contest 
解析:tarjan离线算法求LCA:

概念描述:

LCA(Least Common Ancestors):即最近公共祖先,是指这样一个问题:在有根树中,找出某两个结点u和v的所有祖先中距离(u,v)最近的那个公共祖先(也就是离根最远的那个公共祖先)。

时间和空间复杂度:

  • 时间复杂度:当询问次数为Q,节点数为N时,时间复杂度为O(N+Q)。
  • 空间复杂度:①建图时存储的空间大小(树的节点个数个空间);②深搜时遍历树时需要的空间大小(树的最大深度)

算法实现基于基本原理:

算法是基于DFS并查集来实现的。

算法流程 及 对求LCA(u,v)的证明:

  1. 从根节点(root)开始深搜,
  2. 对于新搜索到的一个节点(x),首先创建由这个结点构成的集合(由par数组维护,par[x]=x,当前这个集合中只有元素x)
  3. 然后依次搜索并处理该节点包含的所有子树(搜素和处理是个递归的过程。结合第5点理解:每搜索完一棵子树,则可确定子树内的LCA询问都已解,其他的LCA询问的结果必然在这个子树之外。)
  4. 当搜索完该节点的所有子树以后,在回溯时,把当前节点的par[x]=(x的父亲节点)(集合归并)
  5. 然后开始处理原先所有询问中包含了该节点的所有询问及求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的更多相关文章

  1. LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现

    首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵 ...

  2. LCA最近公共祖先 Tarjan离线算法

    学习博客:  http://noalgo.info/476.html 讲的很清楚! 对于一颗树,dfs遍历时,先向下遍历,并且用并查集维护当前节点和父节点的集合.这样如果关于当前节点(A)的关联节点( ...

  3. LCA 最近公共祖先 tarjan离线 总结 结合3个例题

    在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...

  4. LCA问题的ST,tarjan离线算法解法

    一  ST算法与LCA 介绍 第一次算法笔记这样的东西,以前学算法只是笔上画画写写,理解了下,刷几道题,其实都没深入理解,以后遇到新的算法要把自己的理解想法写下来,方便日后回顾嘛>=< R ...

  5. LCA(最近公共祖先)离线算法Tarjan+并查集

    本文来自:http://www.cnblogs.com/Findxiaoxun/p/3428516.html 写得很好,一看就懂了. 在这里就复制了一份. LCA问题: 给出一棵有根树T,对于任意两个 ...

  6. 求LCA最近公共祖先的离线Tarjan算法_C++

    这个Tarjan算法是求LCA的算法,不是那个强连通图的 它是 离线 算法,时间复杂度是 O(m+n),m 是询问数,n 是节点数 它的优点是比在线算法好写很多 不过有些题目是强制在线的,此类离线算法 ...

  7. HDU 4547 CD操作 (LCA最近公共祖先Tarjan模版)

    CD操作 倍增法  https://i.cnblogs.com/EditPosts.aspx?postid=8605845 Time Limit : 10000/5000ms (Java/Other) ...

  8. LCA最近公共祖先——Tarjan模板

    LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. Tarjan是一种离线算法,时间复杂度O(n+Q),Q表示询问次数,其中 ...

  9. 最近公共祖先LCA Tarjan 离线算法

    [简介] 解决LCA问题的Tarjan算法利用并查集在一次DFS(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...

随机推荐

  1. stanfordCorenlp在python3中的安装使用+词性学习

    1 安装 前言 Stanford CoreNLP的源代码是使用Java写的,提供了Server方式进行交互.stanfordcorenlp是一个对Stanford CoreNLP进行了封装的Pytho ...

  2. php常用表单验证类用法实例

    <?php /** * 页面作用:常用表单验证类 * 作 者:欣然随风 * QQ:276624915 */ class class_post { //验证是否为指定长度的字母/数字组合 func ...

  3. win10环境变量

    jdk8 JAVA_HOME D:\devsoft\jdk\jdk1.8 CLASSPATH .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar pa ...

  4. SSO单点登录的发展由来以及实现原理【转】

    单点登录以及权限,在很早之前都有写过,不过都比较简单,今天就具体说一下,以及下一步要做的 1.web单系统应用 早期我们开发web应用都是所有的包放在一起打成一个war包放入tomcat容器来运行的, ...

  5. SQLserver连接本地服务器

    1.打开SQLserver “连接到服务器” 2.服务器类型:数据库引擎 3.服务器名称:浏览更多->本地服务器->数据库引擎->选择本地服务器 4.身份验证:windows验证 5 ...

  6. 常用的Oracle的doc命令

    常用的Oracle的doc命令 1.连接数据库 普通用户连接数据库: conn scott/tiger --(默认的用户名/密码).conn 即"connection"连接数据库的 ...

  7. linux 下配置文件目录/etc/sysconfig

    /etc/sysconfig/目录详解 2010-06-19 11:12 6693人阅读 评论(1) 收藏 举报 桌面环境debugging防火墙serviceunix语言 /etc/sysconfi ...

  8. hdu 5877(树状数组+dfs)

    Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total ...

  9. C语言小程序之整除

    看到有人要求用C语言写这样一个小程序,就拿来温习一下 需求:输出从1到2015这2015个自然数中,能被4或5整除,但不能被30整除的数,并计算有多少个数.   #include<stdio.h ...

  10. 作死自救日记——不小心修改linux下/etc/sudoers权限的解决办法

    作死自救日记,献给跟我一样不小心作了死的人 ================================================ 今天不小心作死修改了/etc/sudoers的权限,作死命 ...