洛谷P3379 【模板】最近公共祖先(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


今天(2018.6.6)刚学离线的tarjan做法,复杂度近似为\(O(n)\)

主要思想:利用\(dfs\)的遍历顺序,来实时更新每个时间点某个点的祖先。

我们在遍历到某个节点\(i\)时将这个节点标记已经访问,在 回溯到它父亲时 更新它的祖先(注意时间顺序)。当\((i,j)\)是某个询问时,若\(j\)还未访问,则不执行操作;若\(j\)已经访问,此时\(j\)的祖先即为\((i,j)\)的最近公共祖先。

对于为什么,自己手动模拟一下其实不难明白。

对于祖先关系,我们用并查集维护即可。


code:

#include <cstdio>
const int N=500010;
int n,m,s;
struct Edge
{
    int to,next;
}g[N*2],edge[N*2];
int head1[N],head2[N],cnt1=0,cnt2=0;
void add1(int u,int v)
{
    g[++cnt1].to=v;g[cnt1].next=head1[u];head1[u]=cnt1;
}
void add2(int u,int v)
{
    edge[++cnt2].to=v;edge[cnt2].next=head2[u];head2[u]=cnt2;
}
int ans[N],used[N],f[N];
int find(int x)
{
    return f[x]=f[x]==x?x:find(f[x]);
}
void merge(int x,int y)//y±»ºÏ²¢
{
    f[find(y)]=f[find(x)];
}
void tarjan(int now)
{
    used[now]=1;
    for(int i=head1[now];i;i=g[i].next)
    {
        int v=g[i].to;
        if(!used[v])
        {
            used[v]=1;
            tarjan(v);
            merge(now,v);
        }
    }
    for(int i=head2[now];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(used[v])
            ans[i+1>>1]=find(v);
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    int u,v;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add1(u,v);add1(v,u);
    }
    for(int i=1;i<=n;i++)
        f[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        add2(u,v);add2(v,u);
    }
    tarjan(s);
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

今天复习倍增的,贴一下代码

#include <cstdio>
#include <iostream>
const int N=500010;
int n,m,r;
int head[N],to[N<<1],next[N<<1],cnt;
void add(int u,int v)
{
    to[++cnt]=v;next[cnt]=head[u];head[u]=cnt;
}
int f[N][21],dep[N];
void dfs(int now,int fa)
{
    for(int i=head[now];i;i=next[i])
    {
        int v=to[i];
        if(v!=fa)
        {
            f[v][0]=now;
            dep[v]=dep[now]+1;
            dfs(v,now);
        }
    }
}
int query(int x,int y)
{
    if(dep[x]<dep[y]) std::swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
void init()
{
    for(int j=1;j<=20;j++)
        for(int i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1];
}
int main()
{
    int u,v,a,b;
    scanf("%d%d%d",&n,&m,&r);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dep[r]=1;
    dfs(r,0);
    init();
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",query(a,b));
    }
    return 0;
}