支配树总结

支配树总结

相关概念:

支配:对于一个给定的起点\(r\),当\(u\)是所有到\(v\)路径的必经点时,则称\(u\)支配\(v\)
半必经点:不严谨地讲其含义为在\(x\)的祖先中,能通过非搜索树边而到达\(x\)并且深度最小的点,记为\(semi(x)\)
必经点:记\(idom(x)\)表示所求深度最大的必经点。

最终我们希望求出一颗树,在这颗树中,任意结点都支配其子树,并且被其父亲到父亲的父亲等等支配。

性质:

(以下对点比较大小关系时,都是对点的\(dfn\)进行比较的)

  • \(idom(x)<=semi(x)\)

这个根据相关定义很好证明,如果\(idom(x)>semi(x)\),那么能够存在一条路径绕过\(idom(x)\)

  • 设点集\(P\)\(semi(x)->x\)路径上不包含两端点的点的集合,并且\(t\)是点集\(P\)\(semi\)值最小的点。则有:若\(semi(t)=semi(x)\),则\(idom(x)=semi(x)\);否则,\(idom(x)=idom(t)\).

这个证明就要稍微复杂一些。
首先证明第一种情况,如果此时\(idom(x)!=semi(x)\),根据第一个性质,只有\(idom(x)<semi(x)\),也就是存在一个点\(z\),能够绕开\(semi(x)\)到达\(x\)。那么此时肯定\(z\)会到达\(P\cup x\),此时与\(semi(t)\)最小或者\(semi(x)\)最小不符。

第二种情况的话,因为\(semi(t)!=semi(x)\),那我们分情况考虑:如果\(semi(t)>semi(x)\),脑补一下这种情况不会发生的;那么就只用考虑\(semi(t)<semi(x)\)的情况。

首先证明\(idom(t)\)是必经点:如果不是,那么说明有点\(z\)能够绕过\(idom(t)\)到达\(t\),因为\(idom(t)<=semi(t)\),而\(z<idom(t)\),那么\(semi(t)\)就会变小,与假设不符。
其次证明\(idom(t)\)深度最大,如果存在一个深度更大的必经点\(y\),因为我们首先会到达\(idom(t)\),之后直接从\(idom(t)\)\(t\)最后到\(x\)就行了,也就是说我们可以直接绕过\(y\)
因为\(y\)点的深度一定是不小于\(semi(x)\)的,而\(t\)\(semi(x)\)\(x\)的路径上。

有了第二个性质,那么我们就可以根据\(semi(x)\)以及相关路径上面的\(semi\)值来求\(idom\)值了。

实现

大概说下怎么求出\(semi(x)\)吧。
对于所有的\((u,x)\),如果\(u<x\),则\(semi(x)=min(semi(x),dfn(u))\);否则与\(u\)的一系列满足\(dfn>dfn(x)\)的祖先\(semi\)的最小值进行比较。
第一种情况根据定义显然\(u\)也是一个半必经点,第二种情况可以脑补一下。

具体实现:

  • \(dfs\)求出\(dfn,fa\)数组
  • \(dfn\)从大到小的顺序进行更新,假设现在求\(semi(x)\),在反图上访问所有与\(x\)相邻的\(u\),按照上面所说的进行更新。
  • \(x\)\(fa[x]\)进行合并,并令\(x=fa[x]\),利用第二个性质求出\(idom(x)\)
  • 回到第二步重复此流程
  • 填坑\(idom(x)!=semi(x)\)的残余结点

细节说明:
代码其实还是有许多细节的,我就说说我想到的。
更新的时候,更新\(semi(x)\)要用到\(dfn>dfn(x)\)的点;更新\(idom(x)\)的时候需要\(idom(x)->x\)这条路径上面的\(semi\)信息。所以我们就直接用带权并查集来维护,我们从大到小枚举\(dfn\)可以保证其正确性。
更新完\(semi(x)\)之后,讲\(x\)\(fa[x]\)合并,之后求\(idom(x)\),一开始我这里有点疑惑为什么不先求了再合并,因为性质二中说了不要两端点的,后来发现有这样一个不等式\(min\{semi\}\leq semi(x)\),所以这里先合并也不影响因为\(fa[x]\)的值没有更新,也可以之后再合并。
还有一些细节应该就是建图吧,各种图要区分清楚。

代码:

模板题为例

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5, M = 3e5 + 5;

namespace LT{
    vector <int> G[N], rG[N];
    vector <int> dt[N];     //dominant tree
    int fa[N], best[N], T, n;
    int semi[N], idom[N], dfn[N], idx[N], f[N];
    void init() {
        T = 0;
        for(int i = 1; i <= n; i++) semi[i] = f[i] = best[i] = i;
        for(int i = 1; i <= n; i++) dt[i].clear();
    }
    void dfs(int u) {
        dfn[u] = ++T; idx[T] = u;;
        for(auto v : G[u]) {
            if(!dfn[v]) {
                fa[v] = u; dfs(v);
            }
        }
    }
    int find(int x) {
        if(f[x] == x) return x;
        int fx = find(f[x]);
        if(dfn[semi[best[f[x]]]] < dfn[semi[best[x]]]) best[x] = best[f[x]];
        return f[x] = fx;
    }
    void Tarjan(int rt) {
        dfs(rt);
        for(int i = T; i >= 2; i--) {
            int x = idx[i];
            for(int &u : rG[x]) {
                if(!dfn[u]) continue; //可能原图不能到达
                find(u);
                if(dfn[semi[x]] > dfn[semi[best[u]]]) semi[x] = semi[best[u]];
            }
            f[x] = fa[x];
            dt[semi[x]].push_back(x);
            x = fa[x];
            for(int &u : dt[x]) {
                find(u);
                if(semi[best[u]] != x) idom[u] = best[u];
                else idom[u] = x;
            }
            dt[x].clear();
        }
        for(int i = 2; i <= T; i++) {
            int x = idx[i];
            if(idom[x] != semi[x]) idom[x] = idom[idom[x]];
            dt[idom[x]].push_back(x);
        }
    }
}
int n, m;
int sz[N];
void dfs(int u, int fa) {
    sz[u] = 1;
    for(auto v : LT::dt[u]) {
        if(v == fa) continue;
        dfs(v, u);
        sz[u] += sz[v];
    }
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> m;
    LT::n = n;
    LT::init();
    for(int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        LT::G[u].push_back(v);
        LT::rG[v].push_back(u);
    }
    LT::Tarjan(1);
//    for(int i = 1; i <= n; i++) {
//        cout << i << ':' ;
//        for(auto x : LT::dt[i]) cout << x << ' ';
//        cout << '\n';
//    }
    dfs(1, -1);
    for(int i = 1; i <= n; i++) cout << sz[i] << ' ';
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/heyuhhh/p/11271098.html