• 支配树总结


    支配树总结

    相关概念

    支配:对于一个给定的起点(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)会到达(Pcup 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 << '
    ';
    //    }
        dfs(1, -1);
        for(int i = 1; i <= n; i++) cout << sz[i] << ' ';
        return 0;
    }
    
    
  • 相关阅读:
    LED自动同步时钟(LED电子时钟系统)京准电子
    电力北斗对时装置(北斗时钟同步系统)详细介绍
    京准时间同步系统(NTP网络同步时钟)概述
    京准:NTP时钟服务器(NTP时钟服务器)研究分析
    医院时钟系统(子母钟系统)设计组成架构
    GPS校时器(北斗授时设备)助力政务云建设
    三大免费开源的php语言cms系统 用好它们让你一天建好一个网站
    想精通正则表达式 这几个正则表达式学习资料及工具你必须有!
    零基础建站如何配置PHP运行环境 几种服务器环境配置的选择和方法
    php代码如何加域名授权?开源php项目如何保护版权 商业授权?
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11271098.html
Copyright © 2020-2023  润新知