《支配树》
个人觉得理解起来比较抽象,自己可能也讲不好。
定义:
支配点:对于当前点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 */