zoukankan      html  css  js  c++  java
  • 《支配树》

    个人觉得理解起来比较抽象,自己可能也讲不好。

    定义:

    支配点:对于当前点u,如果删去点v后,点u就无法从起点s到达,那么称v为u的支配点。

    支配树:将每个点和它的最近支配点连边,那么最后就会形成一棵树,称为支配树。

    这个支配点具有类似传递性的性质,a是b的支配点,c是b的支配点,那么必定存在:a是b的支配点或者b是a的支配点。

    所以可以知道对于支配树,从根到u的链上的所有点就是u的所有支配点。

    求解:Lengauer Tarjan 算法

    大致可以看成三个部分:

    1:建立dfs树

    2:建立半支配树

    3:建立支配树

    在此我们需要两个非常重要的数组:idom[i] - 表示i点的最近支配点,也就是在支配树上的父节点。

    semi[i] - 表示i的dfn最小的半支配点,满足对于从u到i的一条路径u -> x1 -> x2 -> x3 .... -> xn -> x,满足$sum_{i = 1}^{n} dfn[xi] > dfn[x] $

    idom[i] - 表示i的最近支配点,即深度最大的支配点

    这里图其实有三种类型:树,DAG,有向带环图。

    这里直接就讲诉对于第三种图的解法,是通用的。

    求解semi数组:

    基于的理论:

    若dfn[v] < dfn[u],那么v可能是u的dfn最小半支配点,一条中间什么都没的链。

    若dfn[v] > dfn[u],那么说明通过semi的传递性(半支配点也是具有传递性的)取最小的半支配点。

    做法如下:

    显然我们如果直接去做,复杂度就会很高。

    那么我们可以看下面这个图,对于4点的semi,除了2- > 3- >4这条路之外的所有都是会被先被dfn走到。

    因为他们的dfn序更大也就是对应第二种情况,对于3 -> 4的这条,3就是4的semi,也就是第一种情况。

    所以我们倒着走dfs序树,然后用带权并查集维护最小的semi即可。

    这里单独对于semi的求解,我们并查集合并的方向其实可以任意。

    但是因为我们后面要求idom,所以我们需要将u合并到fa[u](这里的fa是对于dfs序树里的fa)。

    到这里我们就解决了semi的求解。

    考虑idom求解:我们在求解semi的过程中同时求解idom。

    这里利用我们已经建立的半支配树来求解:
    接下来我们都在半支配树上进行讨论:

    对于点u的一个子节点v:

    如果semi[v] = u,那么说明u就是v的支配点,idom[v] = u.

    否则,idom[v] = semi[v],基于说明semi[v]可以直接到v且不经过u,那么semi[v]就是v的支配点。

    差不多就这样,注意带权并查集上的维护即可。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N = 2e5 + 5;
    const int M = 5e5 + 5;
    const LL Mod = 1e9 + 7;
    #define pi acos(-1)
    #define INF 1e16
    #define dbg(ax) cout << "now this num is " << ax << endl;
    
    int n,m,dfn[N],rk[N],tim = 0,fa[N],f[N],idom[N],semi[N],val[N],sz[N];
    vector<int> G[N],RG[N],T[N];
    void dfs(int u) {
        dfn[u] = ++tim;
        rk[tim] = u;
        for(auto v : G[u]) {
            if(!dfn[v]) {
                fa[v] = u;
                dfs(v);
            }
        }
    }
    int getmin(int x,int y) {
        return dfn[x] < dfn[y] ? x : y;
    }
    int Find(int x) {
        if(x == f[x]) return x;
        int y = f[x];
        f[x] = Find(f[x]);
        if(dfn[semi[val[x]]] > dfn[semi[val[y]]]) val[x] = val[y];
        return f[x];
    }
    void Merge(int x,int y) {//y -> x
        x = Find(x),y = Find(y);
        f[y] = x;
    }
    int main() {
        scanf("%d %d",&n,&m);
        while(m--) {
            int u,v;scanf("%d %d",&u,&v);
            G[u].push_back(v);
            RG[v].push_back(u);
        }
        dfs(1);
        dfn[0] = n + 1;
        for(int i = 1;i <= n;++i) f[i] = i;
    
        for(int i = n;i >= 1;--i) {
            int x = rk[i];
            for(auto v : RG[x]) {
                if(dfn[v] < dfn[x]) semi[x] = getmin(semi[x],v);
                else {
                    Find(v);    
                    semi[x] = getmin(semi[x],semi[val[v]]);
                }
            }
            for(auto v : T[x]) {
                Find(v);
                int ma = val[v];
                if(semi[ma] == x) idom[v] = x;
                else idom[v] = ma;
            }
            val[x] = x;
            Merge(fa[x],x);
            T[semi[x]].push_back(x);
        }
        for(int i = 2;i <= n;++i) {
            int x = rk[i];
            if(idom[x] != semi[x]) idom[x] = idom[idom[x]];
        }
        for(int i = n;i >= 1;--i) {
            int x = rk[i];
            ++sz[x];    
            if(idom[x]) sz[idom[x]] += sz[x];
        }
    
        for(int i = 1;i <= n;++i) printf("%d ",sz[i]);
        system("pause");
        return 0;
    }
    /*
    6 8
    1 2
    2 3
    2 4
    2 6
    3 5
    6 5
    4 5
    1 5
    */
    View Code
  • 相关阅读:
    centos 7 快速安装 nload 流量工具
    centos 快速安装 fish
    OpenMetadata 开放标准的元数据服务
    xxljob 学习
    easy-rules facts 添加扩展数据
    easy-rules-centraldogma-spring-boot-starter 引入外部rule
    cube.js 基于queryRewrite 进行安全控制
    spring prototype bean 获取处理
    基于cloudevents+easy-rules+centraldogma 进行基于规则的业务开发
    mercurius 基于fastify 的graphql server 以及gateway 服务
  • 原文地址:https://www.cnblogs.com/zwjzwj/p/15152061.html
Copyright © 2011-2022 走看看