LCA的Tarjan算法是一个离线算法,复杂度$O(n+q)$。

  我们知道Dfs搜索树时会形成一个搜索栈。搜索栈顶节点cur时,对于另外一个节点v,它们的LCA便是v到根节点的路径与搜索栈开始分叉的那个节点lca。而站在cur上枚举v找lca的过程可以用并查集优化到$O(\log n)$级别。

  并查集的定义:规定v为已经搜索且已经回溯,当前搜索栈顶为cur,则v并查集中的Father为LCA(cur,v)。查询可直接运用该定义。

  并查集的维护:每当搜索栈顶弹出一个节点x时,将x在并查集中的Father设为其在树中的Father。这样x及x的子树的Father就都是这个栈内节点x->Father了。

  注意,不要用vector,全部用邻接表,否则慢。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int MAX_NODE = 500010, MAX_PATH = 500010; struct Node;
struct Edge;
struct Path;
struct Link; struct Node
{
int DfsN;
Edge *Head;
Node *UnFa;//UnionFather
Node *Father;
Link *HeadLink;
}_nodes[MAX_NODE], *Root;
int TotNode; struct Edge
{
Node *To;
Edge *Next;
}_edges[MAX_NODE * 2];
int _eCount; struct Path
{
Node *From, *To;
Node *Lca;
}_paths[MAX_PATH];
int TotPath; struct Link
{
Node *To;
Path *Query;
Link *Next;
}_links[MAX_PATH * 2];
int LinkCnt; void AddEdge(Node *from, Node *to)
{
Edge *e = _edges + ++_eCount;
e->To = to;
e->Next = from->Head;
from->Head = e;
} void AddLink(Node *from, Node *to, Path *query)
{
Link *cur = _links + ++LinkCnt;
cur->To = to;
cur->Query = query;
cur->Next = from->HeadLink;
from->HeadLink = cur;
} void InitAllPath()
{
for (int i = 1; i <= TotPath; i++)
{
AddLink(_paths[i].From, _paths[i].To, _paths + i);
AddLink(_paths[i].To, _paths[i].From, _paths + i);
}
} Node *GetRoot(Node *cur)
{
return cur->UnFa == cur ? cur : cur->UnFa = GetRoot(cur->UnFa);
} void Tarjan(Node *cur, Node *fa)
{
cur->DfsN = 1;
cur->UnFa = cur;
cur->Father = fa;
for (Edge *e = cur->Head; e; e = e->Next)
{
if (e->To == cur->Father)
continue;
Tarjan(e->To, cur);
e->To->UnFa = cur;
}
for (Link *link = cur->HeadLink; link; link = link->Next)
if (link->To->DfsN == 2)
link->Query->Lca = GetRoot(link->To);
cur->DfsN = 2;
} int main()
{
int rootId;
scanf("%d%d%d", &TotNode, &TotPath, &rootId);
Root = _nodes + rootId;
for (int i = 1; i <= TotNode - 1; i++)
{
int u, v;
scanf("%d%d", &u, &v);
AddEdge(_nodes + u, _nodes + v);
AddEdge(_nodes + v, _nodes + u);
}
for (int i = 1; i <= TotPath; i++)
{
int u, v;
scanf("%d%d", &u, &v);
_paths[i].From = _nodes + u;
_paths[i].To = _nodes + v;
}
InitAllPath();
Tarjan(Root, NULL);
for (int i = 1; i <= TotPath; i++)
printf("%lld\n", _paths[i].Lca - _nodes);
return 0;
}

  

luogu3379 【模板】最近公共祖先(LCA) Tarjan的更多相关文章

  1. [模板] 最近公共祖先/lca

    简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...

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

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

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

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

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

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

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

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

  6. POJ 1986 Distance Queries (最近公共祖先,tarjan)

    本题目输入格式同1984,这里的数据范围坑死我了!!!1984上的题目说边数m的范围40000,因为双向边,我开了80000+的大小,却RE.后来果断尝试下开了400000的大小,AC.题意:给出n个 ...

  7. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)

    Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...

  8. POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)

    POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...

  9. POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)

    POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...

  10. 【lhyaaa】最近公共祖先LCA——倍增!!!

    高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...

随机推荐

  1. Django之auth登录认证

    前言:我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统.此时我们需要实现包括用户注册.用户登录.用户认证.注销.修改密码等功能,这还真是个麻烦的事情呢. Django作为一个完美主义者的 ...

  2. No value specified for parameter1?

    我使用的是jdbcTemplate,因为忘记向list中加入参数,所以报错. 解决方案,: String sql = "select * from table where id = ?&qu ...

  3. Git 分支使用

    一个主分支肯定是不够用的,不同的开发最好放在不同的分支上,在最后进行合并,不然在开发中会相互干扰. PS:环境Window xp,Git-1.8.4-preview20130916(http://gi ...

  4. videojs

    <link href="http://vjs.zencdn.net/5.5.3/video-js.css" rel="stylesheet"> &l ...

  5. 我的第一个随笔——MarkDown的简单用法!

    目录 我的第一个笔记 1. 学习简单的markdown语法 1.1 标题 1.2 引用 1.3 倾斜与加粗 1.4无序列表 1.5有序列表 1.6图片和网页 1.7分割线 1.8代码块 1.9结尾 2 ...

  6. vue 安装+下载

    1. npm init -y [生成package.json文件] 2. 增加 "private": true, 3.npm install 4. npm install vue ...

  7. phpcms 搭建宣传网站首页

    1 .修改后台提交的表单信息展示: 文件路径: phpcms\modules\formguide\template\formguide_info_list.tpl.php function getQu ...

  8. 添物零基础到大型全栈架构师 不花钱学计算机及编程(预备篇)— C语言编程基础

    ​C语言介绍 C语言基本是每个编程人员必学的一面语言,很好掌握,是理解编程的关键.很多编程语言基于其编写或者基于此语言的衍生品编写. C语言是人机交互的一个基础语言之一,虽然是之一,单一般其实就是唯一 ...

  9. web & Rich Text Editor

    web & Rich Text Editor 富文本编辑器 http://www.wangeditor.com/ https://github.com/wangfupeng1988/wangE ...

  10. vim高亮显示当前行列

    vim高亮显示当前行: set cursorline vim高亮显示当前列: set cursorcolumn