• 树上倍增法求LCA


    我们找的是任意两个结点的最近公共祖先, 那么我们可以考虑这么两种种情况:

    1.两结点的深度相同.
    2.两结点深度不同.
    第一步都要转化为情况1,这种可处理的情况。

    先不考虑其他, 我们思考这么一个问题: 对于两个深度不同的结点, 把深度更深的那个向其父节点迭代, 直到这个迭代结点和另一个结点深度相同, 那么这两个深度相同的结点的Lca也就是原两个结点的Lca. 因此第二种情况转化成第一种情况来求解Lca是可行的. 这里我们使用倍增法以最快的速度找到相同的深度,然后开始求LCA。求LCA使用倍增法,倍增的条件是找到相同的祖先,减小步距。

    /*
     * LCA在线算法(倍增法) 
     */
    const int MAXN = 10010;
    const int DEG = 20;
    
    struct Edge
    {
        int to, next;
    } edge[MAXN * 2];
    
    int head[MAXN], tot;
    void addedge(int u, int v)
    {
        edge[tot].to = v;
        edge[tot].next = head[u];
        head[u] = tot++;
    }
    
    void init()
    {
        tot = 0;
        memset(head, -1, sizeof(head));
    }
    
    int fa[MAXN][DEG];      //  fa[i][j]表示结点i的第2^j个祖先
    int deg[MAXN];          //  深度数组
    
    void BFS(int root)
    {
        queue<int>que;
        deg[root] = 0;
        fa[root][0] = root;
        que.push(root);
        while (!que.empty())
        {
            int tmp = que.front();
            que.pop();
            for (int i = 1; i < DEG; i++)
            {
                fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1];
            }
            for (int i = head[tmp]; i != -1; i = edge[i].next)
            {
                int v = edge[i].to;
                if (v == fa[tmp][0])
                {
                    continue;
                }
                deg[v] = deg[tmp] + 1;
                fa[v][0] = tmp;
                que.push(v);
            }
        }
    }
    
    int LCA(int u, int v)
    {
        if (deg[u] > deg[v])
        {
            swap(u, v);
        }
        int hu = deg[u], hv = deg[v];
        int tu = u, tv = v;
        for (int det = hv-hu, i = 0; det ; det >>= 1, i++)
        {
            if (det & 1)
            {
                tv = fa[tv][i];
            }
        }
        if (tu == tv)
        {
            return tu;
        }
        for (int i = DEG - 1; i >= 0; i--)
        {
            if (fa[tu][i] == fa[tv][i])
            {
                continue;
            }
            tu = fa[tu][i];
            tv = fa[tv][i];
        }
        return fa[tu][0];
    }
    
    bool flag[MAXN];
    
    int main()
    {
        int T;
        int n;
        int u, v;
        scanf("%d", &T);
    
        while(T--)
        {
            scanf("%d", &n);
            init();
            memset(flag, false, sizeof(flag));
            for (int i = 1; i < n; i++)
            {
                scanf("%d%d", &u, &v);
                addedge(u, v);
                addedge(v, u);
                flag[v] = true;
            }
            int root;
            for (int i = 1; i <= n; i++)
            {
                if (!flag[i])
                {
                    root = i;
                    break;
                }
            }
            BFS(root);
            scanf("%d%d", &u, &v);
            printf("%d
    ", LCA(u, v));
        }
        return 0;
    }
  • 相关阅读:
    斐波那契数列——兔子问题
    最长上升子序列
    洛谷P1325 雷达安装
    K短路
    DIJ的优化,和spfa的优化
    洛谷P5017摆渡车
    洛谷P2258 子矩阵
    三元表达式、列表推导式和生成器表达式
    递归调用、 二分法
    匿名函数、内置函数
  • 原文地址:https://www.cnblogs.com/lunatic-talent/p/12798695.html
Copyright © 2020-2023  润新知