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. 前端QRCode.js生成二维码(解决长字符串模块和报错问题)

    QRCode 用法 1.使用npm安装到你的项目中 npm install qrcode2 --save 使用commonjs或者es6模块方式导入 var QRCode = require('qrc ...

  2. You can fail at what you don't want, so you might as well take a chance on doing what you love.

    You can fail at what you don't want, so you might as well take a chance on doing what you love. 做不想做 ...

  3. sqlmap的使用方法 ——时光凉春衫薄

    普通注入 Sqlmap -u “http://www.xxxxxx.com/xxxx/xxx/xxx.xxx?xx=xx” --dbs 找到一个sql的注入点 探测他的库名   access的直接探表 ...

  4. (转)USB体系结构

    转载地址:http://blog.ednchina.com/zenhuateng/203584/Message.aspx USB总线接口层:物理连接.电气信号环境.信息包传输机制:主机一方由USB主控 ...

  5. ab的使用方法【转】

    使用方法 ab -n 800 -c 800  http://192.168.0.10/ (-n发出800个请求,-c模拟800并发,相当800人同时访问,后面是测试url) ab -t 60 -c 1 ...

  6. LINUX内核中的机制OOM

    [概念] LINUX内核中有一个机制叫做OOM killer(Out Of Memery killer) 该机制监控内存占用过大,尤其是瞬间消耗大量内存的进程, 为了防止内存被耗尽,所以OOM kil ...

  7. 做php网站后台开发,在Linux系统上进行更好吗?【转载】

    1. PHP是开源软件,它在bsd/linux/win下都有很好的正式版及孪生版.并非开发php就必须要在linux下进行.主机服务商们习惯性的把asp与php分为两个主机系列几进行销售.由于asp只 ...

  8. NTP算法

    网络时间协议 由特拉华大学的David L. Mills热心提供.http://www.eecis.udel.edu/~mills mills@udel.edu 由Reinhard v. Hanxle ...

  9. LeetCode301. Remove Invalid Parentheses

    Remove the minimum number of invalid parentheses in order to make the input string valid. Return all ...

  10. JavaScript 兼容性总结

     请实现鼠标点击任意标签,alert该标签的名称(注意兼容性) function elementName(evt){ evt = evt|| window.event; var selected = ...