• 洛谷P3379 【模板】最近公共祖先(LCA)


    题目描述:

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

    题解:

    LCA一般有四种求法:

    1.倍增LCA

    2.LCA转RMQ

    3.树链剖分求LCA

    4.tarjan求LCA(注意只能离线)

    不过呢,我比较喜欢用树剖,因为树剖比较好用,也比较好写,好想,而且实际复杂度比较优。

    树链剖分就是把树剖分成若干条不相交的链

    显然,在链上求LCA很简单,我们只要找到链上两点深度较小的点就行了

    所以在链上比在其他连接链的边上快

    因此实际应用中我们一般会使用“轻重边树链剖分”

    “轻重边树链剖分”指的是将树边分成两类:“重边”和“轻边”

    分类方法如下:

    我们定义Size(x)为以x为根结点的子树的结点个数

    对于每个结点u,在它的所有子结点中寻找一个结点v

    使得对于u的其他子节点w,都有Size(v)≥Size(w)

    此时u有一条重边连向v,有若干条轻边连向u的其他子结点

    这样一来,树上的不在重链上的边的数量便大大减少,效率就提高了

    然后我们每次求LCA(x,y)的时候就可以判断两点是否在同一链上

    如果两点在同一条链上我们只要找到这两点中深度较小的点输出就行了

    如果两点不在同一条链上那就找到深度较大的点令它等于它所在的重链链端的父节点即为x=f[top[x]]

    把树上的重链划分完以后,就可以开始求LCA了。

    树剖要用到两次dfs,都是O(n)的复杂度

    然后如果图是满二叉树的话是最坏情况,但此时查询也是O(logn)的

    最坏情况每次一步步取f[top[x]]走,走下来是一个树的深度,也就是一个logn

    时间复杂度:O(2n+mlogn)

    附上代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int n,m,s;
    int idx,to[1000010],nxt[1000010],head[1000010],f[500010],top[500010],dep[500010],son[500010],siz[500010];
    void addedge(int x,int y)
    {
        ++idx;
        to[idx]=y;
        nxt[idx]=head[x];
        head[x]=idx;
    }
    void dfs1(int x)
    {
        dep[x]=dep[f[x]]+1;
        siz[x]=1;
        for(int i=head[x];i;i=nxt[i])
        {
            if(f[to[i]]==0&&f[x]!=to[i])
            {
                f[to[i]]=x;
                dfs1(to[i]);
                siz[x]+=siz[to[i]];
                if(siz[son[x]]<siz[to[i]])
                    son[x]=to[i];
            }
        }
    }
    void dfs2(int x)
    {
        if(x==son[f[x]])
            top[x]=top[f[x]];
        else
            top[x]=x;
        for(int i=head[x];i;i=nxt[i])
            if(f[to[i]]==x)
                dfs2(to[i]);
    }
    int lca(int x,int y)
    {
        while(top[x]!=top[y])
        {
            if(dep[top[x]]>dep[top[y]])
                x=f[top[x]];
            else
                y=f[top[y]];
        }
        if(dep[x]<dep[y])
            return x;
        else
            return y;
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&s);
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            addedge(x,y);
            addedge(y,x);
        }
        dfs1(s);
        dfs2(s);
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d
    ",lca(x,y));
        }
    }
  • 相关阅读:
    面经 收藏的 这可能不只是一篇面经
    2017网易秋招编程集合
    网易2017春招笔试真题编程题集合题解
    小朋友 排序
    网易编程题目 02 赶去公司
    网易有道2017内推选择题
    12,享元模式(Flyweight Pattern)是以共享的方式高效的支持大量的细粒度的对象。
    11,外观模式(Facade Pattern)是为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
    新手进行Oracle MIS系统开发的步骤
    Oracle常用查看表结构命令(一)
  • 原文地址:https://www.cnblogs.com/jiangminghong/p/9838382.html
Copyright © 2020-2023  润新知