• 图论-最近公共祖先-在线树上倍增


    有关概念:
      最近公共祖先(LCA,Lowest Common Ancestors):对于有根树T的两个结点u、v,最近公共祖先表示u和v的深度最大的共同祖先。

      树上倍增是求LCA的在线算法(对于每一个询问输入后即计算)
    思路:
      fa[i][j]表示编号为j的结点从下往上的第2i个祖先
      即fa[0][j]表示j的父结点,递推式为fa[i][j]=fa[i-1][fa[i-1][j]],即j的第2i个祖先为j的第2i-1个祖先的第2i-1个祖先
      另:不存在第2i个祖先(即2i超过j的深度)时,f[i][j]指向根结点
      求LCA的过程分两步,都类似于逐步逼近,先使指针从深度较深的结点向上逼近直至与另一结点深度相同,再让两个指针同时向上逼近LCA,即求出结果

    样例推导:

    求17、13,16、6,1、15的LCA

    指针1指向13,指针2指向17

    i=0时,指针2移至fa[i][17],即12,使得深度与指针1相同

    i=1时,fa[i][12]==fa[i][13],两指针同时移至5,LCA(17,13)=5

    指针1指向6,指针2指向16

    i=1时,指针2移至fa[i][16],即8,逼近指针1的深度

    i=0时,指针2移至fa[i][8],即5,使得深度与指针1相同

    i=0时,fa[i][5]==fa[i][6],两指针同时移至2,LCA(16,6)=2

    指针1指向1,指针2指向15

    i=2时,指针2移至fa[i][15],即1,使得深度与指针1相同

    发现指针1和指针2重合,LCA(1,15)=1

    不知大家发现没有,指针1开始一直指向深度较小的结点,这个在程序中有体现,主要是减小代码量

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 struct edge
     5 {
     6     int v,next,val;
     7 }e[100005];
     8 int n,m,heads[50005],fa[17][50005],dis[50005],dep[50005],cnt;
     9 void add(int u,int v,int val)
    10 {
    11     e[++cnt].next=heads[u];
    12     heads[u]=cnt;
    13     e[cnt].v=v;
    14     e[cnt].val=val;
    15 }
    16 void dfs(int u)//预处理dep和fa[0][j] 
    17 {
    18     for(int i=heads[u];i;i=e[i].next)
    19     {
    20         if(e[i].v!=fa[0][u])
    21         {
    22             dep[e[i].v]=dep[u]+1;
    23             fa[0][e[i].v]=u;
    24             dis[e[i].v]=dis[u]+e[i].val;
    25             dfs(e[i].v);
    26         }
    27     }
    28 }
    29 int LCA(int u,int v)
    30 {
    31     if(dep[u]>dep[v])swap(u,v);
    32     for(int i=16;~i;i--)
    33         if(dep[fa[i][v]]>=dep[u])
    34             v=fa[i][v];
    35     if(u==v)return u;
    36     for(int i=16;~i;i--)
    37         if(fa[i][u]!=fa[i][v])
    38         {
    39             u=fa[i][u];
    40             v=fa[i][v];
    41         }
    42     return fa[0][u];
    43 }
    44 int main()
    45 {
    46     scanf("%d",&n);
    47     for(int i=1;i<n;i++)
    48     {
    49         int x,y,z;
    50         scanf("%d%d%d",&x,&y,&z);
    51         add(x,y,z);
    52         add(y,x,z);
    53     }
    54     dep[1]=fa[0][1]=1;
    55     dfs(1);
    56     for(int i=1;i<=16;i++)
    57         for(int j=1;j<=n;j++)
    58             fa[i][j]=fa[i-1][fa[i-1][j]];
    59     scanf("%d",&m);
    60     while(m--)
    61     {
    62         int x,y;
    63         scanf("%d%d",&x,&y);
    64         printf("%d
    ",dis[x]+dis[y]-2*dis[LCA(x,y)]);//两点之间路径长度 
    65     }
    66     return 0;
    67 }
  • 相关阅读:
    第三篇:服务提供与Feign调用
    第二篇:服务提供与Rest+Ribbon调用
    第一篇:注册中心Eureka
    先导篇:SpringCloud介绍篇
    将list集合按指定长度进行切分,返回新的List<List<??>>集合
    清空数据的简便语法
    JDK 1.8 新特性之Date-Time API
    读书笔记-我国弱势群体犯罪问题研究
    读书笔记-沙漠里的细水微光
    读书笔记-没有空白
  • 原文地址:https://www.cnblogs.com/xqmmcqs/p/5954097.html
Copyright © 2020-2023  润新知