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;
    }
    

      

  • 相关阅读:
    装载:深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件
    装载: Matlab 提取矩阵 某一行 或者 某一列 的方法
    编码规范的作用
    转载:奇异值分解(SVD) --- 线性变换几何意义(下)
    转载:奇异值分解(SVD) --- 线性变换几何意义(上)
    转载:LBP代码详细注释
    转载:纹理分类(一)全局特征
    转载:LBP的初步理解
    转载:双线性插值
    Matlab位运算操作
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9690555.html
Copyright © 2011-2022 走看看