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(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...
随机推荐
- koa源码阅读[3]-koa-send与它的衍生(static)
koa源码阅读的第四篇,涉及到向接口请求方提供文件数据. 第一篇:koa源码阅读-0第二篇:koa源码阅读-1-koa与koa-compose第三篇:koa源码阅读-2-koa-router 处理静态 ...
- oracle01--单表查询
1. 基本(基础)查询 1.1. 基本查询语法 基本查询是指最基本的select语句. [语法] [知识点]如何使用工具进行查询 在plsql developer中打开查询窗口(执行sql语句): 执 ...
- docker 加速
Docker配置阿里云加速地址 打开阿里云网站https://cr.console.aliyun.com,登陆自己的阿里云账号. 然后只需要在服务器配置docker配置文件,只需要修改"Ex ...
- pythonif语句和循环语句
1.if语句用法 # if语句用法(缩进相同的成为一个代码块) score=90 if score>=60: print("合格") print("OK" ...
- redis基础之redis-sentinel(哨兵集群)(六)
前言 redis简单的主从复制在生产的环境下可能是不行的,因为从服务器只能读不能写,如果主服务器挂掉,那么整个缓存系统不能写入了:redis自带了sentinel(哨兵)机制可以实现高可用. redi ...
- 挂载cifs报错mount error(13): Permission denied(域账号访问时报错)
Linux挂载Windows共享时,报以下错误: mount error(13): Permission deniedRefer to the mount.cifs(8) manual page (e ...
- 图解IIS8上解决ASP.Net第一次访问慢的处理
- Qt跨线程调用错误解析及解决办法
错误提示:Error: Cannot create children for a parent that is in a different thread. 错误案例分析 新建SerialLink子线 ...
- window7 开启自带 ftp
添加 ftp 用户 在windows里添加一个用户.这个其实是你ftp的用户.当然你可以使用匿名访问,但是这样不怎么安全,要知道ftp外网其实也是可以连进来的.去把密码设一下,标准用户就可以了,不用管 ...
- [tensorflow]的安装
1 pip install 最简单直接的方法,通过pip install安装,命令如下: pip install tensorflow-gpu //安装gpu版tensorflow pip insta ...