【简介】

解决LCA问题的Tarjan算法利用并查集在一次DFS(深度优先遍历)中完成所有询问。换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法。

【原理】

先来看这样一个性质:当两个节点(u,v)的最近公共祖先是x时,那么我们可以确定的说,当进行后序遍历的时候,必然先访问完x的所有子树,其中包含u、v,然后才会返回到x所在的节点。这个性质就是我们使用Tarjan算法解决最近公共祖先问题的核心思想。

如上图所示,找出根节点到u得关键路径P ,已遍历的点位于路径P中某个点的子树中,当遍历到u时v已遍历过(u的子树已遍历完),那么v必然存在于子树pk中,此时LCA(u,v)就等于现在v所在集合的祖先pk。如果还没有遍历到,则继续遍历,只不过LCA(u,v)要等到遍历到v时才能知道了,原理如上。需要注意的一点是,为了保持上图的性质,如果一个节点的一个子树遍历完了,需要合并该节点的子树集合。

tarjan算法的步骤是(当dfs到节点u时):

(一) 在并查集中建立仅有u的集合,设置该集合的祖先为u
      (二) 对u的每个孩子v:
                  1. tarjan之
                  2. 合并v到父节点u的集合,确保集合的祖先是u
      (三)设置u为已遍历
      (四)处理关于u的查询,若查询(u,v)中的v已遍历过,则LCA(u,v)=  v所在的集合的祖先

【举例】

假设遍历完10的孩子,要处理关于10的请求了
取根节点到当前正在遍历的节点的路径为关键路径,即1-3-8-10
集合的祖先便是关键路径上距离集合最近的点
比如此时:
    【1,2,5,6】为一个集合,祖先为1,集合中点和10的LCA为1
    【3,7】为一个集合,祖先为3,集合中点和10的LCA为3
    【8,9,11】为一个集合,祖先为8,集合中点和10的LCA为8
    【10,12】为一个集合,祖先为10,集合中点和10的LCA为10

可以发现集合的祖先便是LCA !

【HDU 2586】

换成Tarjan 离线算法来做。

 #pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <string.h>
#include <vector>
#include <cmath>
using namespace std;
int n,m;
struct edge
{
int d,v,next;
edge(){}
edge(int _d,int _v,int _next)
{
d=_d;v=_v;next=_next;
}
}data[];
int map[];
int pool;
void addedge(int s,int e,int v)
{
int t=map[s];
data[pool++]=edge(e,v,t);
map[s]=pool-;
}
int mset[];
int find(int k)
{
if (mset[k]==-) return k;
return mset[k]=find(mset[k]);
}
void uion(int a,int b)
{
int aa=find(a);
int bb=find(b);
mset[aa]=bb;
}
struct _que
{
int a,b;
_que(int q=,int w=){a=q;b=w;}
};
vector<vector<_que> > ques;
vector<int > ans;
int ifv[];
int dis[];
int anc[];
void tar(int cur)
{
ifv[cur]=;
anc[cur]=cur;
int p=map[cur];
while (p!=-)
{
if (!ifv[data[p].d])
{
dis[data[p].d]=dis[cur]+data[p].v;
tar(data[p].d);
uion(cur,data[p].d);
anc[find(cur)]=cur;
}
p=data[p].next;
}
ifv[cur]=;
for (int i=;i<(int)ques[cur].size();++i)
{
if (ifv[ques[cur][i].a]==)
ans[ques[cur][i].b]=dis[cur]+dis[ques[cur][i].a]-*dis[anc[find(ques[cur][i].a)]];
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
ques.clear();
pool=;
memset(map,-,sizeof map);
memset(ifv,,sizeof ifv);
memset(mset,-,sizeof mset);
scanf("%d%d",&n,&m);
ques.resize(n);
int s,e,v;
for (int i=;i<n-;++i)
{
scanf("%d%d%d",&s,&e,&v);
addedge(s-,e-,v);
addedge(e-,s-,v);
}
dis[]=;
ans.resize(m);
for (int i=;i<m;++i)
{
int u,v;
scanf("%d%d",&u,&v);
--u;--v;
ques[u].push_back(_que(v,i));
ques[v].push_back(_que(u,i));
}
tar();
for (int i=;i<(int)ans.size();++i)
{
printf("%d\n",ans[i]);
}
}
}

最近公共祖先LCA Tarjan 离线算法的更多相关文章

  1. LCA最近公共祖先(Tarjan离线算法)

    这篇博客对Tarjan算法的原理和过程模拟的很详细. 转载大佬的博客https://www.cnblogs.com/JVxie/p/4854719.html 第二次更新,之前转载的博客虽然胜在详细,但 ...

  2. LCA(最近公共祖先)--tarjan离线算法 hdu 2586

    HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/ ...

  3. POJ 1470 Closest Common Ancestors (最近公共祖先LCA 的离线算法Tarjan)

    Tarjan算法的详细介绍,请戳: http://www.cnblogs.com/chenxiwenruo/p/3529533.html #include <iostream> #incl ...

  4. POJ1470Closest Common Ancestors 最近公共祖先LCA 的 离线算法 Tarjan

    该算法的详细解释请戳: http://www.cnblogs.com/Findxiaoxun/p/3428516.html #include<cstdio> #include<alg ...

  5. 最近公共祖先LCA(Tarjan算法)的思考和算法实现

    LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...

  6. 最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

    LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...

  7. HDU-2586-How far away(LCA Tarjan离线算法)

    链接:https://vjudge.net/problem/HDU-2586 题意: 勇气小镇是一个有着n个房屋的小镇,为什么把它叫做勇气小镇呢,这个故事就要从勇气小镇成立的那天说起了,修建小镇的时候 ...

  8. 最近公共祖先 LCA Tarjan算法

    来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个 ...

  9. HihoCoder 1067 最近公共祖先(ST离线算法)

    最近公共祖先·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个 ...

随机推荐

  1. Python全栈工程师(多继承、函数重写)

    ParisGabriel                每天坚持手写  一天一篇  决定坚持几年 为了梦想为了信仰    开局一张图 Python人工智能从入门到精通 补充: 对象 --------- ...

  2. hadoop-eclipse环境搭建(二)

    Eclipse插件配置 第一步:把我们的"hadoop-eclipse-plugin-1.0.0.jar"放到Eclipse的目录的"plugins"中,然后重 ...

  3. 使用puTTY或Xshell连接阿里云TimeOut超时

    根据网上很多主流的说法,我依次检查了 ssh: service sshd status 防火墙:service iptables stop (CentOS 7好像已经没有这个iptable了) 都没有 ...

  4. Struts2+DAO层实现实例03——添加监听器跟踪用户行为

    实例说明 根据上两次的成品进行二次加工. 加入Listener,监听用户的登陆注销情况. 所用知识说明 采用SessionBindingListener对Session进行监听. 同时,Action中 ...

  5. # ML学习小笔记—Linear Regression

    Regression Output a scalar Model:a set of function 以Linear model为例 y = b+w * $x_cp$ parameters:b,W f ...

  6. MyBatis 基本演示

    主配置文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration P ...

  7. NetScaler Active-Active模式

    NetScaler Active-Active模式 NetScaler Active-Active模式 (此文档基于版本:NS9.3: Build 55.6 nc) By ShingTan Activ ...

  8. [洛谷P4320]道路相遇

    题目大意:基本同上一题[bzoj5329][Sdoi2018]战略游戏,只是每个点集内只有两个点,且只有一组询问而已.(双倍经验?我反正就直接改了一下代码就交了) 题解:同上一题(链接见“题目大意”) ...

  9. [bzoj] 3263 陌上花开 洛谷 P3810 三维偏序|| CDQ分治 && CDQ分治讲解

    原题 定义一个点比另一个点大为当且仅当这个点的三个值分别大于等于另一个点的三个值.每比一个点大就为加一等级,求每个等级的点的数量. 显然的三维偏序问题,CDQ的板子题. CDQ分治: CDQ分治是一种 ...

  10. [codeforces] 97B Superset || 平面分治

    原题 给出一个平面的一些点,让你添加点,使得所有点对满足以下三个要求中的一个: 1.在一个水平面上 2.在一个竖直线上 3.以这两个点为对角的矩形内包含有其他点 输出一种可行解 因为只需要可行解,且只 ...