【模板】最近公共祖先(LCA)

题目描述

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

输入输出格式

输入格式:

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

输出格式:

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

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=10,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=500000,M<=500000

样例说明:

该树结构如下:

第一次询问:2、4的最近公共祖先,故为4。

第二次询问:3、2的最近公共祖先,故为4。

第三次询问:3、5的最近公共祖先,故为1。

第四次询问:1、2的最近公共祖先,故为4。

第五次询问:4、5的最近公共祖先,故为4。

故输出依次为4、4、1、4、4。

思路:

这道题是最近公共祖先的模板题。。。

很显然,先dfs跑一遍,再由深度浅的往根跳,枚举该点是否合法

那么一个一个往上跳吧?

看一看范围:500000!!!!

TLE的飞起

怎么优化呢?
倍增一下

往上一个成不成立?

不成立

2个呢?

4个呢?
终究会成立

时复大大下降

见代码:

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
struct ljb{
    int from,to;
}tu[1000005];
int head[500005],xyg[1000005],n,m,root,sd[500005],f[500005][20],have[500005],s,t;
float bz;
void read(int &x)
{
    int f=1;
    x=0;
    char s=getchar();
    while(s<'0'||s>'9')
    {
        if(s=='-')
        {
            f=-1;
        }
        s=getchar();
    }
    while(s>='0'&&s<='9')
    {
        x=x*10+s-'0';
        s=getchar();
    }
    x*=f;
}
void shd(int u,int h)
{
    int ltt;
    sd[u]=h;
    for(int i=1;i<=bz;i++)
    {
        if(h<=(1<<i))
        {
            break;
        }
        f[u][i]=f[f[u][i-1]][i-1];
    }
    int k=head[u];
    while(k!=-1)
    {
        ltt=tu[k].to;
        if(!have[ltt])
        {
            have[ltt]=1;        
            f[ltt][0]=u;
            shd(ltt,h+1);
        }
        k=xyg[k];
    }
}
int lca(int ltt,int kkk)
{
    int lzn=sd[ltt];
    int chen_ze=sd[kkk];
    if(lzn!=chen_ze)
    {
        if(lzn<chen_ze)
        {
            swap(ltt,kkk);
            swap(lzn,chen_ze);
        }
        int cha=lzn-chen_ze;
        for(int i=0;i<=bz;i++)
        {
            if((1<<i)&cha)
            {
                ltt=f[ltt][i];
            }
        }
    }
    if(ltt==kkk)
    {
        return kkk;
    }
    int i=0;
    for(i=bz;i>=0;i--)
    {
        if(sd[f[ltt][i]]<0)
        {
            continue;
        }
        if(f[ltt][i]==f[kkk][i])
        {
            continue;
        }
        else
        {
            ltt=f[ltt][i];
            kkk=f[kkk][i];
        }
    }    
    return f[ltt][0];
}
int main()
{
    read(n);
    read(m);
    read(root);
    memset(head,-1,sizeof(head));
    memset(xyg,-1,sizeof(xyg));
    int dsd=2*(n-1);
    for(int i=1;i<=dsd;i+=2)
    {
        read(s);
        read(t);
        tu[i].from=s;
        tu[i].to=t;
        tu[i+1].from=t;
        tu[i+1].to=s;
        xyg[i]=head[s];
        head[s]=i;
        xyg[i+1]=head[t];
        head[t]=i+1;
    }
    bz=log(n)/log(2)+1;
    memset(have,0,sizeof(have));
    memset(sd,0,sizeof(sd));
    memset(f,-1,sizeof(f));
    have[root]=1; 
    shd(root,1);       
    for(register int i=1;i<=n;i++)
    {
        for(register int j=0;j<=bz;j++)
        {
            if(sd[i]<=(1<<j))
            {
                break;
            }
        }
    }
    for(register int i=1;i<=m;i++)
    {
        read(s);
        read(t);
        int y=lca(s,t);
        printf("%d\n",y);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ztz11/p/8972016.html
今日推荐