《支配树》

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

定义:

支配点:对于当前点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

 

posted @ 2021-08-17 14:03  levill  阅读(178)  评论(0编辑  收藏  举报