码迷,mamicode.com
首页 > 其他好文 > 详细

求最近公共祖先LCA两种方法

时间:2020-07-10 10:03:22      阅读:80      评论:0      收藏:0      [点我收藏+]

标签:节点   algorithm   add   math   head   ret   两种方法   swa   最近公共祖先   

tarjan求lca

  • 这种算法本质上是用并查集对向上标记法的优化,是离线算法,即一次性读入所有询问,统一计算,统一输出。
    时间复杂度\(O(n+m)\)

    • v[]进行标记
      \(v[x]\doteq 0\) --> x节点未访问过
      \(v[x]\doteq 1\) --> x节点已经访问,但未回溯
      \(v[x]\doteq 2\) --> x节点已经访问并回溯
  • code

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    const int N = 1e5+5;
    struct side {
        int t, d, next;
    }e[N<<1];
    int head[N], tot;
    void add(int x, int y, int z) {
        e[++tot].next = head[x];
        head[x] = tot;
        e[tot].t = y, e[tot].d = z;
    }
    int n, m, Q, d[N], f[N], v[N], ans[N];
    vector<int> q[N], h[N];
    int found(int x) {
        return x == f[x] ? x : (f[x] = found(f[x]));
    }
    void Dfs(int x) {
        v[x] = 1;
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].t;
            if (v[y]) continue;
            d[y] = d[x] + e[i].d;
            Dfs(y);
            f[y] = x;
        }
        for (int i = 0; i < q[x].size(); i++) {
            int y = q[x][i], id = h[x][i];
            if (v[y] != 2) continue;
            ans[id] = min(ans[id], d[x] + d[y] - 2*d[found(y)]);
        }
        v[x] = 2;
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) f[i] = i;
        while (m--) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z); add(y, x, z);
        }
        scanf("%d", &Q);
        for (int i = 1; i <= Q; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            if (x == y) continue;
            ans[i] = 1 << 30;
            q[x].push_back(y), h[x].push_back(i);
            q[y].push_back(x), h[y].push_back(i);
        }
        Dfs(1);
        for (int i = 1; i <= Q; i++)
            printf("%d\n", ans[i]);
        return 0;
    }
    

倍增求Lca

  • f[x][k]表示从x向根节点走\(2^k\)步到达的节点
    d[x]表示树的深度
    预处理时间复杂度为\(O(n\log n)\),每次询问时间复杂度为\(O(\log n)\)

    • code
      int d[N], f[N][21];
      void dfs(int x, int fa) {
          f[x][0] = fa;
          d[x] = d[fa] + 1;
          for (int i = 1; i <= 20; i++)
              f[x][i] = f[f[x][i-1]][i-1];
          for (int i = head[x]; i; i = e[i].next) 
              if (e[i].t != fa) dfs(e[i].t, x);
      }
      int lca(int x, int y) {
          if (d[x] > d[y]) swap(x, y);
          y = found(y, d[y] - d[x]);
          if (x == y) return x;
          for (int i = 20; i >= 0; i--)
              if (f[x][i] != f[y][i])
                  x = f[x][i], y = f[y][i];
          return f[x][0];
      }
      

推荐

求最近公共祖先LCA两种方法

标签:节点   algorithm   add   math   head   ret   两种方法   swa   最近公共祖先   

原文地址:https://www.cnblogs.com/Z8875/p/13277399.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!