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. Bootstrap Datatable 简单的基本配置

    $(document).ready(function() {     $('#example').dataTable({ "sScrollX": "100%", ...

  2. URL解析-URLComponents

    let components = URLComponents(url: fakeUrl, resolvingAgainstBaseURL: false)! http://10.100.140.84/m ...

  3. gitlab恢复、迁移

    文件说明 安装包:gitlab-ce_8.11.5-ce.0_amd64.deb 备份的数据:533751277_gitlab_backup.tar 系统:Ubuntu 16.04.4 LTS \n ...

  4. Python 开发面试题

    Python部分 将一个字符串逆序,不能使用反转函数 求从10到100中能被3或5整除的数的和 What is Python? What are the benefits of using Pytho ...

  5. LA 3029 City Game

    LA 3029 求最大子矩阵问题,主要考虑枚举方法,直接枚举肯定是不行的,因为一个大矩阵的子矩阵个数是指数级的,因此应该考虑先进行枚举前的扫描工作. 使用left,right,up数组分别记录从i,j ...

  6. 3.2.1.1 POSIX方括号表达式

        为配合非英语的环境,POSIX 标准强化其字符集范围的能力(例如,[a-z]),以匹配非英文字母字符.       POSIX 也在一般术语上作了些变动,我们早先看到的范围表达式在 UNIX  ...

  7. 2.8 补充:shell脚本执行方法

    bash shell 脚本的方法有多种,现在作个小结.假设我们编写好的shell脚本的文件名为hello.sh,文件位置在/data/shell目录中并已有执行权限.   方法一:切换到shell脚本 ...

  8. mySQL and sqoop for ubuntu

    数据的导入导出 ——MySQL & sqoop in Ubuntu 1.完成搭建hadoop集群 2.安装MySQL sudo apt-get install mysql-server mys ...

  9. iOS攻城狮修炼之路

    自己总结的学习iOS的笔记,打造一个全面的知识体系,iOS攻城狮修炼之路[持续更新中] iOS学习笔记01-APP相关 iOS学习笔记02-UIScrollView iOS学习笔记03-UITable ...

  10. POJ 1523 SPF 割点 Tarjan

    SPF Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 9317   Accepted: 4218 Description C ...