• LCA


    概念描述

    LCA(Least Common Ancestors):即最近公共祖先,是指这样一个问题:在有根树中,找出某两个结点u和v的所有祖先中距离(u,v)最近的那个公共祖先(也就是离根最远的那个公共祖先)。

    算法思想:主要有3种 1.倍增(在线) 2.tarjan(离线) 3.RMQ+ST

    1.倍增

    f[i][j]表示i向上跳2的j次方步是谁

    则有f[i][j]=f[f[i][j-1]][j-1]

    注意初始时f[root][i]=root(i-->1-20)

    预处理一遍后

    要找树上两点的LCA

    首先要保证它们在同一深度

    若不在,则用相同“跳”的方式先放在同一深度

    然后在同一深度用跳的方式找到LCA即可

    时间复杂度O(n*logn)

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<algorithm>
    #include<stack>
    #include<cstring>
    using namespace std;
    stack<int> s;
    int f[100001][21],g[100001],dep[100001],n,cnt,T;
    int root1;
    struct node{
        int nxt,to;
    }e[100001];
    bool root[100001];
    inline void addedge(int x,int y){
        e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
    }
    inline void dfs(int u){
        dep[u]=1;s.push(u);
        while(!s.empty()){
            u=s.top();s.pop();
            if(u!=root1) for(int i=1;i<=20;i++)
                        f[u][i]=f[f[u][i-1]][i-1];
            else for(int i=0;i<=20;i++)
                        f[u][i]=u;
            for(int i=g[u];i;i=e[i].nxt){
                if(!dep[e[i].to]){
                    dep[e[i].to]=dep[u]+1;
                    f[e[i].to][0]=u;
                    s.push(e[i].to);
                }
            }
        }
    }
    inline int swim(int x,int h){
        for(int i=0;h;i++,h>>=1)
            if(h&1) x=f[x][i];
        return x;
    }
    inline int lca(int x,int y){
        if(dep[x]<dep[y]){
            int t;
            t=x;
            x=y;
            y=t;
        }
        x=swim(x,dep[x]-dep[y]);
        if(x==y) return x;
        while(1){
            int i;
            for(i=0;f[x][i]!=f[y][i];i++);
            if(!i) return f[x][0];
            x=f[x][i-1];y=f[y][i-1];
        }
    }
    inline void init(){
        int u1,v1;
        memset(root,0,sizeof(root));
        memset(g,0,sizeof(g));
        memset(dep,0,sizeof(dep));
        cnt=0;
        for(int i=1;i<=100000;i++)
            for(int j=0;j<=20;j++)
                    f[i][j]=0;
        scanf("%d",&n);
        for(int i=1,u,v;i<=n-1;i++){
            scanf("%d%d",&u,&v);
            addedge(u,v);
            root[v]=1;
        }
        scanf("%d%d",&u1,&v1);
        for(int i=1;i<=n;i++)
            if(root[i]==0) root1=i;
        f[root1][0]=root1;
        dfs(root1);
        printf("%d
    ",lca(u1,v1));
        return;
    }
    int main(){
        scanf("%d",&T);
        while(T--) 
            init();
        return 0;
    }

    P.S.:不要吐槽为什么是dfs QAQ

    2.tarjan离线

    这里安利一下一篇讲的很好的博客

    http://alanyume.com/130.html

    详细的算法思想就全部在这里了

    然后代码实现。。。

    各种错误调了半天。。。终于答案正确了但是会多输出正确答案(应该和我没有用离线存然后针对这题强行在线输出有关),最后在孙大爷的建议下乱插flag插过了。。。

    由于不会vector所以对怎么离线存储下来还是有点懵逼

    #include<iostream>
    #include<cstdlib>
    #include<algorithm>
    #include<cstdio>
    #include<cstring> 
    using namespace std;
    int ind[100001],g[100001],fa[100001],root,cnt,u,v,n,T,flag;
    bool vis[100001];
    struct node{
        int nxt,to;
    }e[100001];
    inline void addedge(int x,int y){
        e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
    }
    inline int gf(int x){
        if(fa[x]==x) return x;
        else return fa[x]=gf(fa[x]);
    }
    inline void marge(int x,int y){
        int fx=gf(x),fy=gf(y);
        if(fx==fy) return;
        else fa[fy]=fx;
    }
    inline void tarjan(int x){
        if(flag==1) return;
        vis[x]=1;    
        for(int i=g[x];i;i=e[i].nxt){
            tarjan(e[i].to);
            marge(x,e[i].to);
        }
        if(flag) return;
        if(x==u&&vis[v]==1){
            printf("%d
    ",gf(v));
            flag=1;
            return;
        }
        if(flag) return;
        if(x==v&&vis[u]==1){
            printf("%d
    ",gf(u));
            flag=1;
            return;
        }
        if(flag) return;
    }
    inline void clear(){
        memset(fa,0,sizeof(fa));
        memset(g,0,sizeof(g));
        memset(vis,0,sizeof(vis));
        memset(ind,0,sizeof(ind));
        cnt=0;flag=0;
        for(int i=1;i<=n;i++)
            fa[i]=i;
    }
    inline void init(){
        scanf("%d",&n);
        clear();
        for(int i=1;i<=n-1;i++){
            scanf("%d%d",&u,&v);
            addedge(u,v);
            ind[v]=1;
        }
        scanf("%d%d",&u,&v);
        for(int i=1;i<=n;i++) 
            if(ind[i]==0){
                root=i;
                break;
            }
        tarjan(root);
    }
    int main(){
        scanf("%d",&T);
        while(T--){
            init();
        }
        return 0;
    }
  • 相关阅读:
    这鸡汤、真香
    linux 编译安装python并且安装虚拟环境工具
    前端数据删除
    前后端分离DRF项目初始化
    ubuntu 安装nginx docker
    ubuntu安装vue
    虚拟环境安装
    sql语句优化
    Python之网络编程 进程 线程 协程
    Python之网络编程 文件上传 基于udp的协议的socket socketsever同时接收多方消息
  • 原文地址:https://www.cnblogs.com/JimmyC/p/6219066.html
Copyright © 2020-2023  润新知