• lca 模板


      

    #include<bits/stdc++.h>
    using namespace std;
    //input by bxd
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define repp(i,a,b) for(int i=(a);i>=(b);--i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m)
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s);
    #define ll long long
    #define pb push_back
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define inf 0x3f3f3f3f
    const int N=500000+5;
    const int M=2*500000+5;
    struct Edge
    {
        int nex,v,to;
    }edge[M];
    int lg[N],head[M],pos;
    void init(int n)//常数优化
    {
        pos=0;
        CLR(head,0);
        for(int i=1;i<=n;i++)
            lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    }
    void add(int a,int b)
    {
        edge[++pos].nex=head[a];
        head[a]=pos;
        edge[pos].to=b;
    }
    
    int fa[N][100];
    int deth[N];
    void dfs(int cur,int fath)
    {
        deth[cur]=deth[fath]+1;
        fa[cur][0]=fath;
        for(int i=1;(1<<i)<=deth[cur];i++)
        fa[cur][i]=fa[ fa[cur][i-1] ][i-1];
    
        for(int i=head[cur];i;i=edge[i].nex)
        if(edge[i].to!=fath)
            dfs(edge[i].to,cur);
    }
    int lca(int x,int y)
    {
        if(deth[x]<deth[y])
            swap(x,y);
        while(deth[x]>deth[y])
            x=fa[x][ lg[ deth[x]-deth[y] ]-1];
    
        if(x==y)return x;
        for(int k=lg[deth[x]]-1;k>=0;k--)
            if(fa[x][k]!=fa[y][k])
            x=fa[x][k],y=fa[y][k];
    
        return fa[x][0];
    }
    int main()
    {
        int n,m,root;
        RIII(n,m,root);
        init(n);
        rep(i,1,n-1)
        {
            int a,b;RII(a,b);
            add(a,b);add(b,a);
        }
    
        dfs(root,0);
        while(m--)
        {
            int a,b;RII(a,b);
            printf("%d
    ",lca(a,b));
        }
        return 0;
    }
    View Code

    想要实现这个算法,首先我们要记录各个点的深度和他们2^i2i级的的祖先,用数组 m{depth}depth表示每个节点的深度,fa[i][j]fa[i][j]表示节点ii的2^j2j级祖先。 代码如下:

    void dfs(int f,int fath) //f表示当前节点,fath表示它的父亲节点
    {
    depth[f]=depth[fath]+1;
    fa[f][0]=fath;
    for(int i=1;(1<<i)<=depth[f];i++)
      fa[f][i]=fa[fa[f][i-1]][i-1]; //这个转移可以说是算法的核心之一
                                    //意思是f的2^i祖先等于f的2^(i-1)祖先的2^(i-1)祖先
                                    //2^i=2^(i-1)+2^(i-1)
    for(int i=head[f];i;i=e[i].nex)
      if(e[i].t!=fath)
        dfs(e[i].t,f);
    }

    预处理完毕后,我们就可以去找它的LCA了,为了让它跑得快一些,我们可以加一个常数优化(来自洛谷提高组讲义)

    for(int i=1;i<=n;i++) //预先算出log_2(i)+1的值,用的时候直接调用就可以了
      lg[i]=lg[i-1]+(1<<lg[i-1]==i);  //看不懂的可以手推一下

    接下来就是倍增LCA了,我们先把两个点提到同一高度,再统一开始跳。

    但我们在跳的时候不能直接跳到它们的LCA,因为这可能会误判,比如44和88,在跳的时候,我们可能会认为11是它们的LCA,但11只是它们的祖先,它们的LCA其实是33。所以我们要跳到它们LCA的下面一层,比如44和88,我们就跳到44和55,然后输出它们的父节点,这样就不会误判了。

    int lca(int x,int y)
    {
    if(depth[x]<depth[y]) //用数学语言来说就是:不妨设x的深度 >= y的深度
      swap(x,y);
    while(depth[x]>depth[y])
      x=fa[x][lg[depth[x]-depth[y]]-1]; //先跳到同一深度
    if(x==y)  //如果x是y的祖先,那他们的LCA肯定就是x了
      return x;
    for(int k=lg[depth[x]]-1;k>=0;k--) //不断向上跳(lg就是之前说的常数优化)
      if(fa[x][k]!=fa[y][k])  //因为我们要跳到它们LCA的下面一层,所以它们肯定不相等,如果不相等就跳过去。
        x=fa[x][k], y=fa[y][k];
    return fa[x][0];  //返回父节点
    }

    转自洛谷  MorsLin

  • 相关阅读:
    如何查看一个表的块使用状况
    esp8266烧录Html文件,实现内置网页控制设备!
    python一键电影搜索与下载
    基于 Vue BootStrap的迷你Chrome插件
    SpringBoot之自定义验证码
    iOS 图片部分模糊,类似于美图秀秀
    python实现的电影票房数据可视化
    坦克大战-C语言-详注版
    微信小程序-自定义底部导航
    微信小程序--搜索关键词高亮
  • 原文地址:https://www.cnblogs.com/bxd123/p/10815718.html
Copyright © 2020-2023  润新知