zoukankan      html  css  js  c++  java
  • 最近公共祖先

    洛谷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
    ",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
    ",query(a,b));
        }
        return 0;
    }
    
    
  • 相关阅读:
    Selenium+Java自动化之如何优雅绕过验证码
    Java替换中使用正则表达式实现中间模糊匹配
    【转】Jmeter中使用CSV Data Set Config参数化不重复数据执行N遍
    【转】安全测试===如何查看浏览器保存的密码
    JMeter 中_time 函数的使用(时间戳、当前时间)
    TCP拆包粘包之分隔符解码器
    TCP粘包/拆包问题
    Netty 入门示例
    JDK AIO编程
    JDK NIO编程
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9145355.html
Copyright © 2011-2022 走看看