• 图论-最近公共祖先-离线Tarjan算法


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

      Tarjan是求LCA的离线算法(先存储所有询问,再进行运算)

    思路:
      从根结点开始DFS,对遍历到的结点u标记已访问,创建新集合,元素为u,再遍历u的每一个儿子,回溯时将每个儿子的集合并到u的集合上,用并查集记录集合中的每个元素的fa为u,接着处理询问,对于关于u的每一个询问,若另一个结点v已访问,则可断定LCA(u,v)为fav,记录结果,最后按顺序输出即可

      dis存储该结点到根结点的距离,用于计算两点之间的路径长度

    样例推导(样例来自@SHHHS):

    求8、6,9、7的LCA

    从根结点1进入,标记访问,创建集合

    访问2、4,回溯到2,新集合包含两个结点

    访问5、8,处理8的询问,但6未访问,跳过

    访问9,处理询问,7未访问,跳过,5、8和9并入2的集合(即fa值设为2)

    访问6,处理询问,LCA(8,6)为fa8,即2

    访问10,回溯,并入6的集合

    回溯,并入2的集合

    并入1的集合

    访问3、7,处理询问,LCA(7,9)为fa9,即1

    回溯,并入3的集合

    并入1的集合

     1 #include<cstdio>
     2 #define MAXN 
     3 #define MAXQ 
     4 int n,Q,heade[MAXN],headq[MAXN],fa[MAXN],lca[MAXQ],dis[MAXN],cnt;
     5 bool vis[MAXN];
     6 struct edge
     7 {
     8     int v,next,val;
     9 }e[MAXN*2];
    10 struct query
    11 {
    12     int u,v,next;
    13 }q[MAXQ*2];
    14 void adde(int x,int y,int z)
    15 {
    16     e[++cnt].v=y;
    17     e[cnt].next=heade[x];
    18     heade[x]=cnt;
    19     e[cnt].val=z;
    20 }
    21 void addq(int x,int y)
    22 {
    23     q[++cnt].u=x;
    24     q[cnt].v=y;
    25     q[cnt].next=headq[x];
    26     headq[x]=cnt;
    27 }
    28 int getfa(int x)//并查集路径压缩
    29 {
    30     return fa[x]=x==fa[x]?x:getfa(fa[x]);
    31 }
    32 int getdis(int i)//计算路径长度
    33 {
    34     return dis[q[i<<1].u]+dis[q[i<<1].v]-2*dis[lca[i]];
    35 }
    36 void Tarjan(int u)
    37 {
    38     fa[u]=u;
    39     vis[u]=true;
    40     for(int i=heade[u];i;i=e[i].next)
    41     {
    42         int v=e[i].v;
    43         if(!vis[v])
    44         {
    45             dis[v]=dis[u]+e[i].val;
    46             Tarjan(v);
    47             fa[v]=u;
    48         }
    49     }
    50     for(int i=headq[u];i;i=q[i].next)//处理询问
    51     {
    52         int v=q[i].u==u?q[i].v:q[i].u;
    53         if(vis[v])lca[i>>1]=getfa(fa[v]);
    54     }
    55 }
    56 int main()
    57 {
    58     scanf("%d",&n);
    59     int x,y,z;
    60     for(int i=1;i<n;i++)
    61     {
    62         scanf("%d%d%d",&x,&y,&z);
    63         adde(x,y,z);
    64         adde(y,x,z);
    65     }
    66     cnt=1;
    67     scanf("%d",&Q);
    68     for(int i=1;i<=Q;i++)
    69     {
    70         scanf("%d%d",&x,&y);
    71         addq(x,y);
    72         addq(y,x);
    73     }
    74     Tarjan(1);
    75     for(int i=1;i<=Q;i++)
    76     {
    77         printf("%d
    ",getdis(i));
    78     }
    79     return 0;
    80 }
  • 相关阅读:
    I Think I Need a Houseboat
    iOS 8 模糊视图(毛玻璃效果)的简单实现UIVisualEffectView
    freemarker报错之二
    [算法]有趣算法合辑[31-40]
    计算机专业术语全称及含义整理
    JAVA经常使用数据结构及原理分析
    我读经典(6):读《文明之光》有感
    流水号的生成(日期+业务码+自增序列)
    桶排序算法
    3.5星|《哈佛商学院最受欢迎的营销课》:跳出营销红海:逆向战略、超越行业和敌意品牌
  • 原文地址:https://www.cnblogs.com/xqmmcqs/p/5952293.html
Copyright © 2020-2023  润新知