zoukankan      html  css  js  c++  java
  • luogo p3379 【模板】最近公共祖先(LCA)

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

    题意

    • 给一个树,然后多次询问(a,b)的LCA

    模板(主要参考一些大佬的模板)

    #include<bits/stdc++.h>
    //自己的2点:树的邻接链表(静态)表示; lca 的倍增算法
    //优化 log[]
    const int maxn=500010;
    int N,M,S;//S根节点标号
    int head[maxn];//head[i]=k 以i为起点的第一条边是edge[k]
    int dep[maxn],dp[maxn][21];//dp[i][j] i向上走2^j
    int lg[maxn];//(log2(i)+1)
    
    struct edge{
        int v,next;
    };
    edge egs[maxn<<1];
    int k=0;
    void getlg(){
        for(int i=1;i<=n;i++){
            lg[i]=lg[i-1]+((1<<lg[i-1])==i);
        }
    }
    void add(int a,int b){
        //加入边a,b
        egs[k].v=b;
        egs[k].next=head[a];
        head[a]=k++;
    }
    void dfs(int u,int fa){
        dep[u]=dep[fa]+1;
        //printf("db: u %d fa %d dep %d %d
    ",u,fa,dep[u],dep[fa]);
        dp[u][0]=fa;
        for(int i=1;(1<<i)<=dep[u];i++){
            dp[u][i]=dp[dp[u][i-1]][i-1];
        }
        int k;
        for(k=head[u];k!=-1;k=egs[k].next){
            if(egs[k].v!=fa) dfs(egs[k].v,u);
        }
    
    }
    int lca(int a,int b){
        if(dep[a]<dep[b]) std::swap(a,b);
        //dep[a]>=dep[b] a 向上走
        for(int j=20;j>=0;j--){
            if(dep[a]-(1<<j)>=dep[b]){
                a=dp[a][j];
            }
        }
        if(a==b) return a;
        //a,b同时向上走
        for(int i=20;i>=0;i--){
            if(dp[a][i]!=dp[b][i]){
                a=dp[a][i];
                b=dp[b][i];
            }
        }
        return dp[a][0];
    }
    
    
    
    int main(){
        scanf("%d %d %d",&N,&M,&S);
        memset(head,-1,sizeof(head));
        memset(dep,0,sizeof(dep));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<N;i++){
            int a,b;
            scanf("%d %d",&a,&b);
            add(a,b);
            add(b,a);
        }
    
        dfs(S,0);
        /*
        for(int i=0;i<=N;i++){
            printf("db: %d %d
    ",i,dep[i]);
        }
        */
        int x,y;
        for(int i=1;i<=M;i++){
            scanf("%d  %d",&x,&y);
            printf("%d
    ",lca(x,y));
        }
        return 0;
    
    }
    
    

    加了lg[]数组优化的(略微快一点)

    #include<bits/stdc++.h>
    //自己的2点:树的邻接链表(静态)表示; lca 的倍增算法
    //优化 log[]
    const int maxn=500010;
    int N,M,S;//S根节点标号
    int head[maxn];//head[i]=k 以i为起点的第一条边是edge[k]
    int dep[maxn],dp[maxn][21];//dp[i][j] i向上走2^j
    int lg[maxn];//(log2(i)+1)
    
    struct edge{
        int v,next;
    };
    edge egs[maxn<<1];
    int k=0;
    void getlg(){
        for(int i=1;i<=N;i++){
            lg[i]=lg[i-1]+((1<<lg[i-1])==i);
        }
    }
    void add(int a,int b){
        //加入边a,b
        egs[k].v=b;
        egs[k].next=head[a];
        head[a]=k++;
    }
    void dfs(int u,int fa){
        dep[u]=dep[fa]+1;
        //printf("db: u %d fa %d dep %d %d
    ",u,fa,dep[u],dep[fa]);
        dp[u][0]=fa;
        for(int i=1;i<=(lg[dep[u]]-1);i++){
            dp[u][i]=dp[dp[u][i-1]][i-1];
        }
        int k;
        for(k=head[u];k!=-1;k=egs[k].next){
            if(egs[k].v!=fa) dfs(egs[k].v,u);
        }
    
    }
    int lca(int a,int b){
        if(dep[a]<dep[b]) std::swap(a,b);
        //dep[a]>=dep[b] a 向上走
        while(dep[a]>dep[b]){
            a=dp[a][lg[dep[a]-dep[b]]-1];
        }
        if(a==b) return a;
        //a,b同时向上走
        for(int i=(lg[dep[a]]-1);i>=0;){
            if(dp[a][i]!=dp[b][i]){
                a=dp[a][i];
                b=dp[b][i];
                i=lg[dep[a]];
            }
            else i--;
        }
        return dp[a][0];
    }
    
    
    
    int main(){
        scanf("%d %d %d",&N,&M,&S);
        memset(head,-1,sizeof(head));
        memset(dep,0,sizeof(dep));
        memset(dp,0,sizeof(dp));
        getlg();
        for(int i=1;i<N;i++){
            int a,b;
            scanf("%d %d",&a,&b);
            add(a,b);
            add(b,a);
        }
    
        dfs(S,0);
        /*
        for(int i=0;i<=N;i++){
            printf("db: %d %d
    ",i,dep[i]);
        }
    */
        int x,y;
        for(int i=1;i<=M;i++){
            scanf("%d  %d",&x,&y);
            printf("%d
    ",lca(x,y));
        }
        return 0;
    
    }
    
    
    

    值得注意的问题

    • 初始化的位置
    • 树的邻接链表表示(真的比较省内存而且好用)
    • lca的倍增算法(还可以求树上两点距离)
  • 相关阅读:
    XML参考 :XmlReader 详解、实例(2) 读取XML节点
    JavaScript: Cookie 详解、实例与应用
    什么是Cookie?Cookie有什么作用?
    JavaScript: indexOf 详解、实例与应用
    XML参考 :XmlReader 详解、实例(3) 读取XML节点和属性名称
    XML参考 :XmlReader 详解、实例(1) 详解
    C# 参考:泛型(1) 泛型简介与泛型问题陈述
    Visual Studio 2003/Visual Studio 2005常用快捷键
    XML参考 :XmlReader 详解、实例(4) 读取XML内容
    Page.trace 跟踪调试 详解
  • 原文地址:https://www.cnblogs.com/fridayfang/p/9532296.html
Copyright © 2011-2022 走看看