• TarjanLCA学习笔记


    1.前言

      首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为)。

    2.思想

      下面详细介绍一下Tarjan算法的思想:

          1.任选一个点为根节点,从根节点开始。

          2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。

          3.若是v还有子节点,返回2,否则下一步。

          4.合并v到u上。

          5.寻找与当前点u有询问关系的点v。

          6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。

      从上面步骤可以看出,Tarjan算法要用到并查集。这里,我们使用链式前向星来存储边和询问情况。

    3.算法实现

    不妨以洛谷P3379:LCA模板题为例题

     1 #include<cstdio>
     2 #include<cstring>
     3 
     4 const int maxn = 5e5 + 10,maxm = 5e5 + 10;
     5 int fa[maxn],fir[maxn],frt[maxn],ans[maxm],n,m,s,tot,id;
     6 bool flag[maxn];
     7 //存边
     8 struct Edge{
     9     int v,next;
    10 }e[maxn << 1];
    11 //存询问
    12 struct Ques{
    13     int v,next,qid;
    14 }q[maxm << 1];
    15 //加边
    16 void addEdge(int u,int v){
    17     e[tot] = (Edge){v,fir[u]};
    18     fir[u] = tot++;
    19     e[tot] = (Edge){u,fir[v]};
    20     fir[v] = tot++;
    21 }
    22 //加询问
    23 void addQues(int u,int v,int qid){
    24     q[id] = (Ques){v,frt[u],qid};
    25     frt[u] = id++;
    26     q[id] = (Ques){u,frt[v],qid};
    27     frt[v] = id++;
    28 }
    29 //读入优化
    30 inline int read(){
    31     int sum = 0;
    32     char ch = getchar();
    33     while(ch < '0' || ch > '9') ch = getchar();
    34     while(ch >= '0' && ch <= '9'){sum = sum * 10 + ch - '0'; ch = getchar();}
    35     return sum;
    36 }
    37 //输出优化
    38 void write(int x){
    39     if(x / 10) write(x / 10);
    40     putchar(x % 10 + '0');
    41 }
    42 //寻找父亲
    43 int find(int x){
    44     if(fa[x] != x) fa[x] = find(fa[x]);
    45     return fa[x];
    46 }
    47 //合并
    48 void unionn(int x,int y){
    49     int fx = find(x),fy = find(y);
    50     fa[fx] = fy;
    51 }
    52 //离线操作(u表示当前点,fa表示该点的前驱点)
    53 void dfs(int u,int fa){
    54     flag[u] = true;//标记已访问
    55     //遍历所有与u相连的点
    56     for(int i = fir[u];i != -1;i = e[i].next){
    57         int v = e[i].v;
    58         //如果v不是u的前驱
    59         if(v != fa){
    60             dfs(v,u);//遍历下一层
    61             //寻找是否有询问关系
    62             for(int j = frt[v];j != -1;j = q[j].next){
    63                 int k = q[j].v;
    64                 if(flag[k])
    65                     ans[q[j].qid] = find(k);
    66             }
    67             unionn(v,u);//连接
    68         }
    69     }
    70 }
    71 //主函数
    72 int main(){
    73     n = read(),m = read(),s = read();
    74     memset(fir,-1,sizeof(fir));
    75     memset(frt,-1,sizeof(frt));
    76     for(int i = 1;i < n;i++){
    77         fa[i] = i;
    78         int x = read(),y = read();
    79         addEdge(x,y);
    80     }
    81     fa[n] = n;
    82     for(int i = 1;i <= m;i++){
    83         int x = read(),y = read();
    84         addQues(x,y,i);
    85     }
    86     dfs(s,-1);
    87     //因为这里已经给出根了,直接从根开始
    88     for(int i = 1;i <= m;i++,putchar('
    ')) write(ans[i]);
    89     return 0;
    90 }

      

  • 相关阅读:
    437. Path Sum III
    51. N-Queens
    dfs 感悟
    Topological Sorting
    138 Copy List with Random Pointer
    130. Surrounded Regions
    The sum problem
    A + B Again
    Rectangles
    An easy problem
  • 原文地址:https://www.cnblogs.com/water-mi/p/7774649.html
Copyright © 2020-2023  润新知