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

    题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

    整体步骤:1.使两个点深度相同;2.使两个点相同。

    这两个步骤都可用倍增法进行优化。定义每个节点的Elder[i]为该节点的2^k(或者说是二进制中的1,10,100,1000...)辈祖先。求它时要利用性质:cur->Elder[i]==cur->Elder[i-1]->Elder[i-1]。具体步骤看代码。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int MAX_FA = 10, MAX_NODE = 500010, MAX_EDGE = MAX_NODE * 2;
    
    struct Node;
    struct Edge;
    
    struct Node
    {
    	int Id, Depth;
    	Edge *Head;
    	Node *Elder[MAX_FA];
    }_nodes[MAX_NODE], *Root;
    int TotNode;
    
    struct Edge
    {
    	Node *From, *To;
    	Edge *Next;
    }*_edges[MAX_EDGE];
    int _edgeCnt;
    
    void Init(int root, int totNode)
    {
    	_edgeCnt = 0;
    	TotNode = totNode;
    	Root = _nodes + root;
    	memset(_nodes, 0, sizeof(_nodes));
    }
    
    Edge *NewEdge()
    {
    	return _edges[++_edgeCnt] = new Edge();
    }
    
    void AddEdge(Node *from, Node *to)
    {
    	Edge *e = NewEdge();
    	e->From = from;
    	e->To = to;
    	e->Next = from->Head;
    	from->Head = e;
    }
    
    void Build(int uId, int vId)
    {
    	Node *u = _nodes + uId, *v = _nodes + vId;
    	u->Id = uId;
    	v->Id = vId;
    	AddEdge(u, v);
    	AddEdge(v, u);
    }
    
    int Log2(int x)
    {
    	int cnt = 0;
    	while (x / 2)
    	{
    		cnt++;
    		x /= 2;
    	}
    	return cnt;
    }
    
    void Dfs(Node *cur, Edge *FromFa)
    {
    	if(!FromFa)
    		cur->Depth = 1;
    	else
    	{
    		cur->Elder[0] = FromFa->From;
    		cur->Depth = cur->Elder[0]->Depth + 1;
    		for(int i=1; cur->Elder[i-1]->Elder[i-1]; i++)
    			cur->Elder[i] = cur->Elder[i-1]->Elder[i-1];
    	}
    	for(Edge *e = cur->Head; e; e=e->Next)
    		if(e->To!=cur->Elder[0])
    			Dfs(e->To, e);
    }
    
    void DfsStart()
    {
    	Dfs(Root, NULL);
    }
    
    Node *Lca(Node *deep, Node *high)
    {
    	if (deep->Depth < high->Depth)
    		swap(deep, high);
    	int len = deep->Depth - high->Depth;
    	for(int k=0; len; k++)
    	{
    		if((1 << k) & len)
    		{
    			deep=deep->Elder[k];
    			len -= (1 << k);//把len二进制中当前的1去掉
    		}
    	}
    	if (deep == high)
    		return deep;
    	for (int k = Log2(deep->Depth); k >= 0; k--)
    	{
    		if (deep->Elder[k] != high->Elder[k])
    		{
    			deep = deep->Elder[k];
    			high = high->Elder[k];
    		}
    	}
    	return deep->Elder[0];
    }
    
    int main()
    {
    	int totNode, totQ, rootId, uId, vId, id1, id2;
    	scanf("%d%d%d", &totNode, &totQ, &rootId);
    	Init(rootId, totNode);
    	for (int i = 1; i < totNode; i++)
    	{
    		scanf("%d%d", &uId, &vId);
    		Build(uId, vId);
    	}
    	DfsStart();
    	for (int i = 1; i <= totQ; i++)
    	{
    		scanf("%d%d", &id1, &id2);
    		printf("%d
    ", Lca(id1 + _nodes, id2 + _nodes)->Id);
    	}
    	return 0;
    }
    

      

    对Lca中for循环正确性的解释:每个整数都可以表示为sum(2^k)。所以以此方式可以到达一个节点的任意辈祖先。

    注意:

    • k初值有log。
    • 树的深度和高度要区分开来。
    • Dfs时一开始循环中的判断cur->[k-1]!=NULL是为了处理根节点。
  • 相关阅读:
    最长双回文串
    BUUOJ misc 二维码
    HDU 1284 钱币兑换问题 (动态规划 背包方案数)
    HDU 1260 Tickets (动态规划)
    HDU 1231 最大连续子序列 (动态规划)
    HDU 1203 I NEED A OFFER! (动态规划、01背包、概率)
    BUUOJ reverse SimpleRev (爆破)
    BUUOJ reverse 不一样的flag
    HDU 1176 免费馅饼 (动态规划、另类数塔)
    HDU 1171 Big Event in HDU (动态规划、01背包)
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8454899.html
Copyright © 2011-2022 走看看