zoukankan      html  css  js  c++  java
  • 【codevs2370】小机房的树——倍增法/链剖求LCA

    题目链接

    这道题就是LCA的完全模版题了,之前学倍增的时候写过一次,但是太久没复习忘了......

    滚回来重新学了一波,赶紧写篇博客记录一下~

    倍增法求LCA的基本思路:先dfs预处理出每个节点i的祖先2^j表示为gr[i][j]、深度deep[i]以及到祖先2^j 的距离dis[i][j]。然后对于每个询问x和y的最近公共祖先,先钦定(?)y在x的下方,然后先让y跳到x这一层,而(1<<i)&d巧妙地运用位运算让y能恰好用最少步数跳到x这一层。如果此时x==y即说明x是y的祖先,可以直接返回答案;否则就从大到小枚举倍增,这个地方也是倍增法的核心,要好好理解其中的巧妙之处。

    要注意的地方:

    1,根节点为0;

    2.gr[i][0]即为节点i的父节点;

    3.当y跳到x这一层时要注意判断x是否为y的祖先;

    4.倍增结束时要记得x和y还要跳到他们的父节点上,ans不要忘记加上dis[x][0]和dis[y][0]。

    其他细节详见代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    const int M=5e4+10;
    using namespace std;
    struct point{
        int to,next,w;
    }e[M*2];
    int n,m,sum=0,u,v,wi,first[M],deep[M];
    bool ok[M];
    int gr[M][22],dis[M][22];
    int read()
    {
        int ans=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();}
        return ans*f;
    }
    void add(int x,int y,int z)
    {
        sum++;e[sum].to=y;e[sum].w=z;e[sum].next=first[x];first[x]=sum;
        sum++;e[sum].to=x;e[sum].w=z;e[sum].next=first[y];first[y]=sum;
    }
    void dfs(int x)
    {
        ok[x]=1;
        for(int i=1;i<=20;i++){
            if((1<<i)>deep[x])break;
            gr[x][i]=gr[gr[x][i-1]][i-1];
            dis[x][i]=dis[x][i-1]+dis[gr[x][i-1]][i-1];
        }
        for(int i=first[x];i>0;i=e[i].next){
            int now=e[i].to;
            if(ok[now])continue;
            deep[now]=deep[x]+1;
            gr[now][0]=x;
            dis[now][0]=e[i].w;
            dfs(now);
        }
    }
    inline int lca(int x,int y)
    {
        int ans=0;
        if(deep[x]>deep[y])swap(x,y);
        int d=deep[y]-deep[x];
        for(int i=0;i<=20;i++)
            if((1<<i)&d){
                ans+=dis[y][i];
                y=gr[y][i];
            }
        if(x==y)return ans;
        for(int i=20;i>=0;i--){
            if(gr[x][i]!=gr[y][i]){
                ans+=dis[x][i]+dis[y][i];
                x=gr[x][i];y=gr[y][i];
            }
        }
        ans+=dis[x][0]+dis[y][0];
        return ans;
    }
    int main()
    {
        memset(first,-1,sizeof(first));
        n=read();int a,b;
        for(int i=1;i<=n-1;i++){
            u=read();v=read();wi=read();
            add(u,v,wi);
        }
        dfs(0);
        m=read();
        while(m--){
            a=read();b=read();
            printf("%d
    ",lca(a,b));
        }
        return 0;
    }
    codevs2370

     2017/9/21:

    今天跟着ccz学了一波树链剖分求lca,理解完之后发现链剖无论是时间还是空间都完踩倍增诶>.<

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 const int N=5e4+10;
     5 int tot=0,first[N],n,m;
     6 int dis[N];
     7 struct node{
     8     int ne,to,w;
     9 }e[N*2];
    10 struct point{
    11     int son,fa,top,sz,dep;
    12 }q[N];
    13 void read(int &x){
    14     int f=1;x=0;char c=getchar();
    15     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    16     while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
    17     x*=f;
    18 }
    19 void ins(int u,int v,int w){
    20     e[++tot]=(node){first[u],v,w};first[u]=tot;
    21     e[++tot]=(node){first[v],u,w};first[v]=tot;
    22 }
    23 void dfs(int x,int fa){
    24     point&w=q[x];
    25     w.sz=1;
    26     for(int i=first[x];i;i=e[i].ne){
    27         int to=e[i].to;
    28         if(to!=fa){
    29             point&u=q[to];dis[to]=dis[x]+e[i].w;
    30             u.dep=w.dep+1;u.fa=x;
    31             dfs(to,x);w.sz+=u.sz;if(q[w.son].sz<u.sz)w.son=to;
    32         }
    33     }
    34 }
    35 void dfs1(int x,int tp){
    36     point&w=q[x];w.top=tp;
    37     for(int i=first[x];i;i=e[i].ne){
    38         int to=e[i].to;
    39         if(to!=w.fa){
    40             if(to==w.son)dfs1(to,tp);
    41             else dfs1(to,to);
    42         }
    43     }
    44 }
    45 int lca(int x,int y){
    46     int a=q[x].top,b=q[y].top;
    47     while(a!=b){
    48         if(q[a].dep>q[b].dep)x=q[a].fa,a=q[x].top;
    49         else y=q[b].fa,b=q[y].top;
    50     }
    51     if(q[x].dep<q[y].dep)return x;
    52     return y;
    53 }
    54 int main(){
    55     read(n);
    56     for(int i=1,a,b,c;i<n;i++){
    57         read(a);read(b);read(c);a++;b++;ins(a,b,c);
    58     }
    59     dis[1]=0;dfs(1,0);dfs1(1,1);
    60     read(m);
    61     while(m--){
    62         int u,v;read(u);read(v);u++;v++;
    63         printf("%d
    ",dis[u]+dis[v]-2*dis[lca(u,v)]);
    64     }
    65     return 0;
    66 }
    链剖求lca
  • 相关阅读:
    PHP 使用 GET 传递数组变量
    Java实现 蓝桥杯 算法训练 数据交换
    Java实现 蓝桥杯 算法训练 数据交换
    Java实现 蓝桥杯 算法训练 数据交换
    Java实现 蓝桥杯 算法训练 景点游览
    Java实现 蓝桥杯 算法训练 景点游览
    Java实现 蓝桥杯 算法训练 景点游览
    Java实现 蓝桥杯 算法训练 二进制数数
    Java实现 蓝桥杯 算法训练 二进制数数
    Java实现 蓝桥杯 算法训练 二进制数数
  • 原文地址:https://www.cnblogs.com/JKAI/p/7301523.html
Copyright © 2011-2022 走看看