zoukankan      html  css  js  c++  java
  • POJ 1330 Nearest Common Ancestors(LCA模板)

    给定一棵树求任意两个节点的公共祖先

    tarjan离线求LCA思想是,先把所有的查询保存起来,然后dfs一遍树的时候在判断。如果当前节点是要求的两个节点当中的一个,那么再判断另外一个是否已经访问过,如果访问过的话,那么它的最近公共祖先就是当前节点祖先。

    下面是tarjan离线模板:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 10010;
    
    struct Edge {
        int to, next;
    }edge[maxn * 2];
    //查询 
    struct Query {
        int q, next;
        int index;
    }query[maxn * 2];
    
    int tot, head[maxn];
    //查询的前向星 
    int cnt, h[maxn];
    //查询的答案保存在ans中 
    int ans[maxn * 2];
    int fa[maxn];//并查集 
    int r[maxn];//并查集集合个数 
    int ancestor[maxn];//祖先 
    bool vis[maxn];//访问标记 
    int Q;//查询总数 
    void init(int n)
    {
        tot = 0;
        cnt = 0;
        Q = 0;
        memset(h, -1, sizeof(h));
        memset(head, -1, sizeof(head));
        memset(fa, -1, sizeof(fa));
        memset(ancestor, 0, sizeof(ancestor));
        memset(vis, false, sizeof(vis));
        for (int i = 1; i <= n; i++) r[i] = 1;
    }
    void addedge(int u, int v)
    {
        edge[tot].to = v;
        edge[tot].next = head[u];
        head[u] = tot++;
    }
    void addquery(int u, int v, int index)
    {
        query[cnt].q = v;
        query[cnt].index = index;
        query[cnt].next = h[u];
        h[u] = cnt++;
    }
    int find(int x)
    {
        if (fa[x] == -1) return x;
        return fa[x] = find(fa[x]);
    }
    void Union(int x, int y)
    {
        int t1 = find(x);
        int t2 = find(y);
        if (t1 != t2)
        {
            if (t1 < t2)
            {
                fa[t1] = t2;
                r[t2] += r[t1];
            }
            else
            {
                fa[t2] = t1;
                r[t1] += r[t2];
            }
        }
    }
    void LCA(int u)//tarjan离线算法 
    {
        vis[u] = true;
        ancestor[u] = u;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if (vis[v]) continue;
            LCA(v);
            Union(u, v);
            ancestor[find(u)] = u;
        }
        for (int i = h[u]; i != -1; i = query[i].next)
        {
            int v = query[i].q;
            if (vis[v])
            {
                ans[query[i].index] = ancestor[find(v)];
            }
        }
    }
    bool in[maxn];
    int main()
    {
        int T, n;
        scanf("%d", &T);
        while (T--)
        {
            scanf("%d", &n);
            init(n);
            memset(in, false, sizeof(in));
            int u, v;
            for (int i = 1; i < n; i++)
            {
                scanf("%d %d", &u, &v);
                in[v] = true;
                addedge(u, v);
                addedge(v, u);
            }
            scanf("%d %d", &u, &v);
            addquery(u, v, Q);//添加查询 
            addquery(v, u, Q++);
            int root;
            for (int i = 1; i <= n; i++) 
            {
                if (!in[i])
                {
                    root = i;
                    break;
                }
            }
            LCA(root);
            for (int i = 0; i < Q; i++)//按照顺序打印出来答案 
                printf("%d
    ", ans[i]);
        }
        return 0;
    }

    RMQ&LCA在线模板:

    RMQ st算法是用来求一段连续的区间最值问题的,如果将树看成一个线性结构,那么它可以快速求出一段区间的最值,那么就可以利用它求出LCA,首先求出一个树的欧拉序列(就是dfs序),然后每个节点都有深度,都有到根节点的距离。保存一个第一次访问到某个节点的编号。这样求两个点的LCA就是求从欧拉序列当中的一段到另外一段(连续的)深度的最小值。直接RMQ就可以了。模板如下:

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int maxn = 20010;
    int tot, head[maxn];
    struct Edge {
        int to, next;
    }edge[maxn];
    int occur[maxn];
    int first[maxn];
    int dep[maxn];
    bool vis[maxn];
    int m;
    void init()
    {
        tot = 0;
        memset(head, -1, sizeof(head));
        memset(vis, false, sizeof(vis));
        memset(first, 0, sizeof(first));
        m = 0;
    }
    void addedge(int u, int v)
    {
        edge[tot].to = v;
        edge[tot].next = head[u];
        head[u] = tot++;
    }
    void dfs(int u, int depth)
    {
        occur[++m] = u;
        dep[m] = depth;
        if (!first[u])
            first[u] = m;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            dfs(v, depth + 1);
            occur[++m] = u;
            dep[m] = depth;
        }
    }
    int Rmin[maxn * 2][32];
    void RMQ(int n)
    {
        for (int i = 1; i <= n; i++)
            Rmin[i][0] = i;
        int k = (int)log2(n);
        for (int j = 1; j <= k; j++)
        {
            for (int i = 1; i + (1 << j) - 1 <= n; i++)
                Rmin[i][j] = dep[Rmin[i][j - 1]] < dep[Rmin[i + (1 << (j - 1))][j - 1]] ? Rmin[i][j - 1] : Rmin[i + (1 << (j - 1))][j - 1];
        }
    }
    int query(int a, int b)
    {
        int l = first[a], r = first[b];
        if (l > r)
            swap(l, r);
        int k = (int)log2(r - l + 1);
        int tmp = dep[Rmin[l][k]] < dep[Rmin[r - (1 << k) + 1][k]] ? Rmin[l][k] : Rmin[r - (1 << k) + 1][k];
        return occur[tmp];
    }
    int main()
    {
        int T, n;
        scanf("%d", &T);
        while (T--)
        {
            init();
            scanf("%d", &n);
            int a, b;
            for (int i = 1; i < n; i++)
            {
                scanf("%d %d", &a, &b);
                addedge(a, b);
                vis[b] = true;
            }
            int root;
            for (int i = 1; i <= n; i++)
            {
                if (!vis[i])
                {
                    root = i;
                    break;
                }
            }
            dfs(root, 1);
            scanf("%d %d", &a, &b);
            RMQ(m);
            printf("%d
    ", query(a, b));
        }
        return 0;
    }
  • 相关阅读:
    Java实现 LeetCode 792 自定义字符串排序(暴力)
    Java实现 LeetCode 792 自定义字符串排序(暴力)
    asp.net session对象的持久化
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    小白也能看懂的约瑟夫环问题
  • 原文地址:https://www.cnblogs.com/Howe-Young/p/4871821.html
Copyright © 2011-2022 走看看