LCA最近公共祖先(倍增版)

牧云@^-^@ 提交于 2020-03-27 02:03:56

倍增版LCA

lac即最近公共祖先,u和v最近公共祖先就是两节点公用的祖先中深度最大的
比如


其中
lca(1,2)=4,
lca(2,3)=4,
lca(3,5)=1,
lca(2,5)=4;

如何求LCA?

树上倍增版:

  1. 预处理每一个节点的深度depth[i];
  2. 选定两节点;
  3. 将深度大的节点往上跳,跳到与另一节点相同深度;
  4. 然后两个节点一起往上跳,直到两个节点重合;
  5. 那么这个节点就是两个节点的lca;

那么怎么让计算机实现“跳”?

我们可以用倍增思想
f[i][j]表示第i个节点往上跳2^j个节点所到达的祖先
那么
f[i][j]=f[f[i][j-1]][j-1]
意思是
从i往上跳2j个节点所到达的节点可以转化为从i往上跳2(j-1)再往上跳2^(j-1)个,而f[i][j-1]在之前通过递推已经推出来了
初始化Code:

void init()
{
    for(int j=1;j<=N;j++)
    {
        for(int i=1;i<=n;i++)
        f[i][j]=f[f[i][j-1]][j-1];
    }
}

题目

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

输入格式:

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

输出格式:

输出包含M行,每行包含一个正整数,依次为每一个询问的结果。

输入样例#1:
    5 5 4
    3 1
    2 4
    5 1
    1 4
    2 4
    3 2
    3 5
    1 2
    4 5
输出样例#1:
    4
    4
    1
    4
    4
说明

时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=10000,M<=10000
对于100%的数据:N<=500000,M<=500000

#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
int head[500010],depth[500010];
int n,m,s,cnt,f[500010][20];
int N=0;
bool vis[500010];
struct Edge
{
    int next;
    int to;
}e[500010];
inline int Read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();}
    return x*f;
}
void add(int from,int to)
{
    cnt++;
    e[cnt].to=to;
    e[cnt].next=head[from];
    head[from]=cnt;
}
void dfs(int x)
{
    vis[x]=true;
    for(int i=head[x];i;i=e[i].next)
	{
        int now=e[i].to;
        if(!vis[now])
		{
	        depth[now]=depth[x]+1;
	        f[now][0]=x;
	        dfs(now);
    	}
    }
}
void init()
{
    for(int j=1;j<=N;j++)
    {
        for(int i=1;i<=n;i++)
        f[i][j]=f[f[i][j-1]][j-1];
    }
}
int lca(int x,int y)
{
    if(depth[x]>depth[y]) swap(x,y);
    int d=depth[y]-depth[x];
    for(int i=0;i<=N;i++)
	{
        if((1<<i) & d)
        y=f[y][i];
    }
    if(x==y) return x;
    for(int i=N;i>=0;i--)
	{
        if(f[x][i]!=f[y][i])
		{
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
int main()
{
	n=Read();m=Read();s=Read();
	N=log(n)/log(2)+1;
    for(int i=1;i<n;i++)
	{
    	int x,y;
		x=Read();y=Read();
        add(x,y);
        add(y,x);
    }
    dfs(s);
    init();
    while(m--)
	{
    	int x,y;
		x=Read();y=Read();
        printf("%d\n",lca(x,y));
    }
    return 0;
}

据我所知LCA的算法不只一种,比如:
ST表算法
Tarjan算法
树链剖分算法

倍增法只是其中一种在线算法,但已经够用
其他算法请小伙伴们自行查阅吧!

大图预警!!!

镇楼

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!