zoukankan      html  css  js  c++  java
  • luogu3379 【模板】最近公共祖先(LCA) Tarjan

      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
    ", _paths[i].Lca - _nodes);
    	return 0;
    }
    

      

  • 相关阅读:
    Arrays.asList的使用
    php之sql语句 创建数据库、表、插入字段,自动判断是否成功
    初识 canvas 绘图
    自定义音频audio播放器
    我的晨练
    js获取屏幕或可视范围
    js 查看脚本运行时间的办法
    a:hover伪类在ios移动端浏览器内触发无法取消
    js模拟用户触摸事件
    持续健身带来的变化
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9690555.html
Copyright © 2011-2022 走看看