• 最近公共祖先(LCA)


    算法一:朴素法

    采用两点逐渐向根移动的方法,求出LCA。

    具体步骤:
    1.求出每个节点的深度
    2.询问两个节点是否重合,若重合,则LCA已经求出。否则,选择两个点中深度较大一个,移动它的父亲。

    时间复杂度(O(n)),查询(q)次的话,复杂度(O(qn))

    优点:简单,该算法允许树动态改变。

    代码如下:

    
    struct EDGE{int to,nxt;}e[MAXN];
    void addedge(int u,int v){e[++cnt].to=v; e[cnt].nxt=adj[u]; adj[u]=cnt;}
    //建树的过程
    //同时求出每个节点的深度,与每个节点的父亲
    void dfs(int u,int father)
    {
    	fat[u]=father; deep[u]=deep[father]+1;
    	for(int i=adj[u];i;i=e[i].nxt)
    	{
    		int v=e[i].to;
    		if(v!=father) dfs(v,u);
    	}
    	return ;
    }
    //相当于模拟移动
    int LCA(int u,int v)
    {
    	while(u!=v)
    	{
    		if(deep[u]>=deep[v]) u=fat[u];
    		else v=fat[v];
    	}
    	return u;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&s);
    	for(int i=1;i<=n-1;++i)
    	{
    		int u,v;scanf("%d%d",&u,&v);
    		addedge(u,v); addedge(v,u);
    	}
    	dfs(s,s);
    	for(int i=1;i<=m;++i)
    	{
    		int a,b; scanf("%d%d",&a,&b);
    		printf("%d
    ",LCA(a,b));
    	}
    	return 0;
    }
    

    算法二 倍增LCA

    第一步:初始化(建树)

    求出倍增数组 (anc)。(和一开始建树的思路一样)

    其中,MAXLOG是最小的满足(2^x leq MAXDEEP)(最深的节点深度)的(x)

    (anc[i][j])(i)节点向上走(2^j)后能走到的点。

    我们规定根节点的父亲就是它自己,这样根节点往上走还是根节点。

    对于(anc) 而言,有如下的递推关系:

    ( anc[i][j]=left{egin{matrix} anc[i][j] & j=0 \ anc[anc[i][j-1]][j-1]& j> 1 end{matrix} ight. )

    解释:

    第一个,(j=0)就是根节点。

    第二个,从(anc)的意义出发:节点(i)向上走(2^{j-1})步后,再走(2^{j-1})步,加起来就是(2^j)次步。

    第二步:把两个节点移动到同一深度

    假设(LCA(x,y))不失一般性,令(deep[x] /leq deep[y])

    先让x往上走(deep[x]-deep[y])步。

    我们将这个差值转化为二进制,然后,通过通过倍增数组往上走(2)的幂次步。
    (即:对于二进制为(1)的第(i)位,往上走(2^i)步)

    或者,还有一种方法:

    从大到小扫描(i),如果(anc[x][i])的深度不小于(y),就跳(x)

    第三步: 求LCA

    在第二步的处理后,假设(x,y)往上走最小的(L)步后是同一节点,也就意味着,(x,y)向上走最大的(L-1)步,是不同的节点。

    我们可以从大到小的枚举(2^i)步,如果当前(x,y)向上走(2^i)步后为同一点,则停止。否则就一起往上走。
    (由于倍增数组的特性,该过程可以看成二分)

    直到不能走为止,再往上走一步就是(LCA)。(PS:(2^0=1)

    时间复杂度:(O((n+q)log_2(n)))

    优点:在线查询,支持动态树。

    代码如下:

    #include <bits/stdc++.h>
    #define MAXN 10000005
    #define MAXLOG 32
    using namespace std;
    int n,m,s;
    int deep[MAXN],anc[MAXN][MAXLOG];
    int cnt=0,adj[MAXN];
    struct EDGE
    {
    	int to,nxt;
    }	e[MAXN];
    void addedge(int u,int v)
    {
    	e[++cnt].to=v; e[cnt].nxt=adj[u]; adj[u]=cnt;
    }
    void dfs(int u,int father)
    {
    	anc[u][0]=father;	deep[u]=deep[father]+1;
    	for(int i=1;i<MAXLOG;++i) anc[u][i]=anc[ anc[u][i-1] ][i-1]; 
    	for(int i=adj[u];i;i=e[i].nxt)
    	{
    		int v=e[i].to;
    		if(v!=father) dfs(v,u);
    	}
    	return ;
    }
    int LCA(int u,int v)
    {
        if(deep[u]<deep[v])
            swap(u,v);
        int h=deep[u]-deep[v];
        for(int i=0;i<MAXLOG;++i) 
        {
            if(h&(1<<i))
            {
                u=anc[u][i];
            }
        }
        if(u==v)
            return u;
        for(int i=MAXLOG-1;i>=0;--i) // -1!!!
        {
            if(anc[u][i]!=anc[v][i])
            {
                u=anc[u][i];
                v=anc[v][i];
            }
        }
        return anc[u][0];
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&s);
    	for(int i=1;i<=n-1;++i)
    	{
    		int u,v;scanf("%d%d",&u,&v);
    		addedge(u,v); addedge(v,u);
    	}
    	dfs(s,s);
    	for(int i=1;i<=m;++i)
    	{
    		int a,b; scanf("%d%d",&a,&b);
    		printf("%d
    ",LCA(a,b));
    	}
    	return 0;
    }
    
  • 相关阅读:
    福建工程学院第十四届ACM校赛B题题解
    2018 ACM-ICPC青岛现场赛 B题 Kawa Exam 题解 ZOJ 4059
    联合周赛第二场 我在哪?题解
    维修数列 Splay(这可能是我写过最麻烦的题之一了。。。用平衡树维护dp。。。丧心病狂啊。。。。)
    虚树入门!世界树!
    御坂御坂题解(出自北航校赛) 约瑟夫环问题高效解决方案
    网络流24题! 开始!题解!
    AFO
    【模板库】减维的模板库【停更】
    【组合数学】Educational Codeforces Round 83 (Rated for Div. 2) D题
  • 原文地址:https://www.cnblogs.com/cyl-oi-miracle/p/13871561.html
Copyright © 2020-2023  润新知