• Tarjan求LCA总结


    Tarjan算法
    向上标记法:
    从x向上走到根节点,并标记所有经过的点
    从y向上走到根节点,当第一次遇到已标记的节点时,就找到了LCA(x, y)
    对于每个询问,向上标记法的时间复杂度最坏为O(n)

    在深度遍历的任意时刻,我们将树中的节点分成三类:
    1.我们已经访问了,但是我们还没有回溯的节点标记为1
    2.我们访问过并且已经回溯到的,标记为2
    3.没有访问过的节点
    对于正在访问的节点x,他的父节点是标记为1的。若y是已经访问并且回溯的节点,则LCA(x, y)就是由y向上走,遇到的第一个标记为1的节点。
    我们很容易想到可以使用并查集优化。
    当一个节点标记为2时,我们把它合并到他父亲所在的集合(此时他的父亲一定标记为1且单独构成一个集合)
    这就相当于每个完成回溯的几点都有一个指向它的父节点的指针,只需查询y所在集合的代表元素(并查集的get操作),就等价于从y向上一直走到一个开始递归但未回溯的节点,即LCA(x, y)
    其实整个过程,自己在演草纸上画一遍就好了(建议换一篇博客看看)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn = 500086;
     4 struct shiki {
     5     int y, net;
     6 }e[maxn << 1];
     7 struct enkidu {
     8     int self, id, nex; 
     9 }ask[maxn << 1];
    10 int n, m, s;
    11 int lin[maxn], len = 0;
    12 int both[maxn], tot = 0;
    13 int fa[maxn], lca[maxn];
    14 int vis[maxn];
    15 
    16 inline int read() {
    17     int x = 0, y = 1;
    18     char ch = getchar();
    19     while(!isdigit(ch)) {
    20         if(ch == '-') y = -1;
    21         ch = getchar();
    22     }
    23     while(isdigit(ch)) {
    24         x = (x << 1) + (x << 3) + ch - '0';
    25         ch = getchar();
    26     }
    27     return x * y;
    28 }
    29 
    30 inline void insert(int xx, int yy) {
    31     e[++len].y = yy;
    32     e[len].net = lin[xx];
    33     lin[xx] = len;
    34 }
    35 
    36 inline void add(int xx, int yy, int i) {
    37     ask[++tot].self = yy;
    38     ask[tot].id = i;
    39     ask[tot].nex = both[xx];
    40     both[xx] = tot;
    41 }
    42 
    43 int getfather(int x) {
    44     if(x == fa[x]) return x;
    45     return fa[x] = getfather(fa[x]);
    46 }
    47 
    48 void LCA_tarjan(int x) {
    49     vis[x] = 1;
    50     for(int i = lin[x]; i; i = e[i].net) {
    51         int to = e[i].y;
    52         if(vis[to]) continue;
    53         LCA_tarjan(to);
    54         fa[to] = x;
    55     }
    56     for(int i = both[x]; i; i = ask[i].nex) {
    57         int to = ask[i].self;
    58         if(vis[to] == 2)
    59             lca[ask[i].id] = getfather(to);
    60     }
    61     vis[x] = 2;
    62 }
    63 
    64 int main() {
    65     memset(vis, 0, sizeof(vis)); 
    66     n = read(), m = read(), s = read();
    67     for(int i = 1; i < n; ++i) {
    68         int x, y;
    69         x = read(), y = read();
    70         insert(x, y);
    71         insert(y, x); 
    72     }
    73     for(int i = 1; i <= n; ++i) fa[i] = i;
    74     for(int i = 1; i <= m; ++i) {
    75         int x, y;
    76         x = read(), y = read();
    77         add(x, y, i);
    78         add(y, x, i);
    79     }
    80     LCA_tarjan(s);
    81     for(int i = 1; i <= m; ++i)    
    82         cout << lca[i] << '
    ';
    83     return 0;
    84 } 
    关于板子,它救活了
  • 相关阅读:
    CA证书申请、认证原理
    流量异常监控
    排查和处理一台被攻击的linux系统及其事后分析
    day39---mysql基础三
    一次专利讲座的笔记
    HBase MVCC 代码阅读(一)
    [翻译]HBase 中的 ACID
    [翻译]HBase 的 MVCC 和内建的原子操作
    HBase Maven 工程模块梳理
    关于 MVCC 的基础
  • 原文地址:https://www.cnblogs.com/ywjblog/p/9488047.html
Copyright © 2020-2023  润新知