• [luogu5180]支配树


    对于有向图$G$和起点$s$,有以下定义和性质——

    为了方便,不妨假设$s$能到达$G$中所有点,并任意建立一棵以$s$为根的dfs树,以下节点比较默认均按照两点在这棵dfs树上的dfs序

    支配点:$x$是$t$的支配点当且仅当将$x$以及相关的边删除后,不存在$s$到$t$的路径,也称作$x$支配$t$

    (特别的,约定$s$和$t$均是$t$的支配点)

    支配树:一棵以$s$为根的树,满足$t$的支配点恰是$t$到根的路径上所有点

    引理1:若$x<y$,则$x$到$y$的路径(若存在)必然经过其$x$和$y$公共祖先

    可以简单归纳证明,具体过程略

    引理2:对于$t\ne s$,支配树上$t$的父亲为dfs树上$t$最深(且不为本身)的支配点(支配树存在且唯一)

    注意到$t$的支配点必然是$t$或$t$在dfs树上的祖先,因此显然成立

    记该点为$idom_{t}$,问题即如何求$idom$

    半支配点:$x$是$t$的半支配点当且仅当存在一条$x$到$t$的路径,满足路径中所有点(除起点和终点外)均大于$x$

    引理3:上述定义中,"所有点(除起点和终点外)均大于$x$"等价于"所有点(除起点外)均不是$x$的祖先

    必要性显然,充分性根据引理1也显然

    记$t$的半支配点中最小的为$semi_{t}$,考虑如何求$semi$

    性质1:若$t\ne s$,则$semi_{t}=\min\{\min_{(x,t)\in E}x,\min_{k>t,k子树中\exists (x,t)\in E}semi_{k}\}$

    若$(x,t)\in E$,显然$x$是$t$的半支配点,即有$semi_{t}\le x$

    若$k>t$且$k$子树中$\exists (x,t)\in E$,那么构造路径$semi_{k}\rightarrow k\rightarrow x\rightarrow t$,显然$semi_{k}$也是$t$的半支配点,即有$semi_{t}\le semi_{k}$

    结合两者,即得到左式$\le $右式

    另一方面,考虑$semi_{t}$到$t$路径上最后一条边$(x,t)$,对其分类讨论:

    1.若$x=semi_{t}$,即有$semi_{t}\ge \min_{(x,t)\in E}x$

    2.若$x\ne semi_{t}$,取$semi_{t}$到$t$路径上$x$最浅的祖先$k$(除起点外),再考虑到$k$之前的这一段路径,结合引理3可得$semi_{t}$也是$k$的支配点,即有$semi_{t}\ge \min_{k>t,k子树中\exists (x,t)\in E}semi_{k}$

    两者包含了所有情况,因此又得到左式$\ge$右式,即得证

    引理4:若$t\ne s$,则$idom_{t}$是$semi_{t}$或其祖先,$semi_{t}$是$t$的祖先

    前者根据定义显然,后者考虑$fa_{t}$总是$t$的半支配点,因此$semi_{t}\le fa_{t}<t$,再根据引理1也显然

    性质2:若$t\ne s$,则$idom_{t}=\begin{cases}semi_{t}&(semi_{u}=semi_{t})\\idom_{u}&(semi_{u}<semi_{t})\end{cases}$(其中$semi_{u}=\min_{u\in 树链(semi_{t},t]}semi_{u}$)

    (树链$(semi_{t},t]$指在dfs树上从$semi_{t}$到$t$的路径上除$semi_{t}$的点集)

    (注意到$u$可以取$t$,那么显然$semi_{u}\le semi_{t}$)

    对两种情况分别证明——

    第一种情况下,注意到$semi_{t}$已经是$idom_{t}$的"下界",那么只需要证明其支配$t$即可

    若$semi_{t}=s$显然成立,否则考虑反证法:

    假设删除$semi_{t}$以及相关的边后还存在一条$s$到$t$的路径,考虑路径上最后一个是$semi_{t}$祖先的点$x$和之后第一个在树链$(semi_{t},t]$上的点$u$(由于$s$和$t$分别满足两者的条件,因此总存在)

    考虑$x$到$u$的这一段,结合引理3可得$x$也是$u$的半支配点,与$semi_{u}$的最小性矛盾

    第二种情况下,若$idom_{t}$不支配$u$,那么从$s$到$u$再到$t$即可(注意$idom_{t}\le semi_{t}<u$),再由$idom_{u}$的最小性得$idom_{t}\le idom_{u}$,因此$idom_{u}$同样是"下界",那么只需要证明其支配$t$即可

    类似地定义$x$和$u$(将$semi_{t}$均变为$idom_{u}$),注意到$u$必然还在树链$(semi_{t},t]$上(否则显然$idom_{u}$不是$u$的支配点),进而同理$x$也是$u$的半支配点,与$semi_{u}$的最小性矛盾

    另外,其实性质2有以下类似地描述(理解)方式——

    性质2':若仅保留树边和$(semi_{t},t)$的边(其中$t\ne s$),得到的新图$G'$支配树与$G$相同

    综合上述分析,来考虑具体的实现:

    求$semi$——根据性质1,从后往前依次求,即支持单点修改、查询单点到根路径极值,由于其形式的特殊性可以用带权并查集维护,时间复杂度为$o(n\log n)$(仅路径压缩的并查集)

    求$idom$——根据性质2,类似地求即可(注意顺序,需按照$semi$从后往前求出$u$,再从前往后求出$idom$),时间复杂度同样为$o(n\log n)$

    最后建出支配树,再简单求和即可,时间复杂度为$o(n)$

    最终,总复杂度为$o(n\log n)$​,可以通过

    (另外,本题中若$s$不能到达$t$,则$t$的支配点仅定义为$s$和$t$)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 200005
     4 vector<int>v[N],rev_v[N],Q[N];
     5 int n,m,x,y,dfn[N],pos[N],Fa[N],fa[N],mn[N],semi[N],idom[N],ans[N];
     6 int get_min(int x,int y){
     7     if (dfn[x]<dfn[y])return x;
     8     return y;
     9 }
    10 int get_min_semi(int x,int y){
    11     if (dfn[semi[x]]<dfn[semi[y]])return x;
    12     return y;
    13 }
    14 int find(int k){
    15     if (k==fa[k])return k;
    16     int x=find(fa[k]);
    17     mn[k]=get_min_semi(mn[k],mn[fa[k]]);
    18     return fa[k]=x;
    19 }
    20 void update(int k){
    21     fa[k]=Fa[k],mn[k]=k;
    22 }
    23 int query(int k){
    24     find(k);
    25     return mn[k];
    26 }
    27 void dfs(int k){
    28     dfn[k]=++dfn[0],pos[dfn[0]]=k;
    29     for(int i=0;i<v[k].size();i++)
    30         if (!dfn[v[k][i]]){
    31             Fa[v[k][i]]=k; 
    32             dfs(v[k][i]);
    33         }
    34 }
    35 int main(){
    36     scanf("%d%d",&n,&m);
    37     for(int i=1;i<=m;i++){
    38         scanf("%d%d",&x,&y);
    39         v[x].push_back(y);
    40         rev_v[y].push_back(x);
    41     }
    42     dfs(1);
    43     int nn=dfn[0]++;
    44     for(int i=1;i<=n;i++)fa[i]=i,mn[i]=0;
    45     for(int i=nn;i>1;i--){
    46         int t=pos[i];
    47         for(int j=0;j<rev_v[t].size();j++){
    48             semi[t]=get_min(semi[t],rev_v[t][j]);
    49             semi[t]=get_min(semi[t],semi[query(rev_v[t][j])]);
    50         }
    51         Q[dfn[semi[t]]].push_back(t);
    52         for(int j=0;j<Q[i].size();j++){
    53             int t=Q[i][j];
    54             idom[t]=query(t);
    55         }
    56         update(t);
    57     }
    58     for(int i=0;i<Q[1].size();i++){
    59         int t=Q[1][i];
    60         idom[t]=query(t);
    61     }
    62     for(int i=2;i<=nn;i++){
    63         int t=pos[i];
    64         if (semi[idom[t]]==semi[t])idom[t]=semi[t];
    65         else idom[t]=idom[idom[t]];
    66     }
    67     for(int i=1;i<=n;i++)ans[i]=1;
    68     for(int i=nn;i>1;i--){
    69         int t=pos[i];
    70         ans[idom[t]]+=ans[t];
    71     }
    72     ans[1]=n;
    73     for(int i=1;i<n;i++)printf("%d ",ans[i]);
    74     printf("%d\n",ans[n]);
    75     return 0;
    76 } 
    View Code
  • 相关阅读:
    安装Bind到CentOS(YUM)
    安装Ansible到CentOS(YUM)
    安装AB到CentOS(YUM)
    安装APACHE到CentOS(YUM)
    07 Spring Cloud Eureka是什么?
    06 Spring Boot Starter的介绍及使用
    05 Spring Boot项目搭建步骤(超详细)
    04 Spring Cloud开发环境的准备和Lombok安装步骤
    03 Spring Cloud和Dubbo的区别及各自的优缺点
    02 Spring Cloud 模块介绍
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15588797.html
Copyright © 2020-2023  润新知