zoukankan      html  css  js  c++  java
  • [整理]支 配 树

    感觉天天做题智商并没有提高,于是颓废之余瞎学点东西。
    不保证对除我自己之外的任何人易懂。

    0.一些定义

    为了方便叙述,以下涉及到节点大小比较的地方无特殊说明则直接用节点编号代替 ( ext{dfn})(DFS 树上的时间戳)。
    在一个从给定起点出发能到达任意一个点的有向图上,一个点 (i) 支配(j) 当且仅当删去 (i) 后不存在起点到 (j) 的路径,显然一个点的支配点不一定只有一个。
    方便起见,在 DFS 树上我们采用 Tarjan 的一些表示方法:
    (u o v) 表示 (u) 直接连到 (v)
    (uxrightarrow{.}v) 表示 (u)(v) 的祖先;
    (uxrightarrow{+}v) 表示 (uxrightarrow{.}vland u e v)
    以下罗列的一些引理或定理一般都可以直接一步反证出来,所以略去了绝大部分证明,如果难以理解可以自己画画图(我就是这么做的)。
    另外,无特殊说明时默认节点不是起点。
    定义节点 (u)直接支配点 ( ext{idom}_u) 为支配 (u) 的点中 ( ext{dfn}) 最大的点。
    引理一:根据定义显然 ( ext{idom}_uxrightarrow{+}u)
    定义节点 (u)半支配点 ( ext{sdom}_u) 为最小的节点 (v) 满足原图中存在一条 (v)(u) 的路径,路径上每个点(除 (v))都大于 (u)
    引理二:显然 ( ext{sdom}_uxrightarrow{+}u)

    1.一些有用的结论

    引理三:显然 ( ext{idom}_uxrightarrow{.} ext{sdom}_u)
    引理四:(forall uxrightarrow{.}v,uxrightarrow{.} ext{idom}_vlor ext{idom}_vxrightarrow{.} ext{idom}_u)
    有了这些引理,我们就可以导出一些有用的定理。
    定理一:(forall v ext{ s.t. } ext{sdom}_uxrightarrow{+}vxrightarrow{.}u, ext{sdom}_vge ext{sdom}_u),则 ( ext{idom}_u= ext{sdom}_v)
    可以性感地理解为所有 (v) 都被封闭在 ( ext{sdom}_u)(u) 里面了,找不出一个 (v) 使得删去 ( ext{sdom}_u) 后还能从上面走到 (v),也就是 ( ext{sdom}_u) 支配 (u)
    定理二:(exists v ext{ s.t. } ext{sdom}_uxrightarrow{+}vxrightarrow{.}u, ext{sdom}_vle ext{sdom}_u),则 ( ext{sdom}) 最小的点 (p) 满足 ( ext{idom}_p= ext{idom}_u)
    显然 ( ext{idom}_u) 一定在 ( ext{sdom}_p) 上面,而上面最大的点就是 ( ext{idom}_p)
    然后我们把上面两个揉起来就得到推论:
    推论一:设满足 ( ext{sdom}_uxrightarrow{+}vxrightarrow{.}u) 的点中 ( ext{sdom}) 最小的 (v)(p),那么有

    [ ext{idom}_u=egin{cases} ext{sdom}_p& ext{sdom}_u= ext{sdom}_p,\ ext{idom}_p& ext{otherwise.}end{cases} ]

    所以 ( ext{idom}) 可以通过 ( ext{sdom}) 求出来,那么 ( ext{sdom}) 怎么求呢?我们有以下解法:
    找到所有原图中连向 (u)(v),所有 (<u)(v)(>u)(v)(>u) 的祖先的半支配点都可能是 (u) 的半支配点(主语是 (<u)(v) 和半支配点)。性感理解发现它显然是充分的。
    通过这些东西理论上就可以完整地求出直接支配点了,塔老师意料之中地又一次给出了一个神奇的实现:
    对于半支配点,我们倒序枚举,由于需要考虑祖先,我们自然想到用并查集维护,带上一个最小半支配点的权值。
    然后我们建出半支配树后按照推论一直接计算直接支配点即可,这一步可以利用求半支配点时留下的信息。

    2.代码实现

    洛谷 P5180 【模板】支配树
    实现的时候需要注意一个细节:处理直接支配点时枚举完父亲要清掉父亲。

    const int N=200010,M=300010;
    int n,m;
    struct Graph {
      struct Edge {
        int to,nxt;
      }e[M];
      int hd[N],cnt;
      il void ade(int u,int v){
        e[++cnt].to=v,e[cnt].nxt=hd[u],hd[u]=cnt;
      }
      Graph(){
        memset(hd,0,sizeof hd),cnt=0;
      }
    }G,R,D;//原图、反图、半支配树
    int dfn[N],idx[N],fa[N],tim;
    void DFS(int u,int ff){
      idx[dfn[u]=++tim]=u,fa[u]=ff;
      for(rg int i=G.hd[u];i;i=G.e[i].nxt){
        int v=G.e[i].to;
        if(!dfn[v])DFS(v,u);
      }
    }
    //直接支配/半支配/父亲/权值
    int idm[N],sdm[N],f[N],mn[N];
    int F(int k){//并查集维护最小sdom
      if(f[k]==k)return k;
      int ff=F(f[k]);
      if(dfn[sdm[mn[f[k]]]]<dfn[sdm[mn[k]]])mn[k]=mn[f[k]];
      return f[k]=ff;
    }
    void Tarjan(){
      for(rg int i=1;i<=n;i++)f[i]=mn[i]=sdm[i]=i;
      for(rg int i=tim;i>1;i--){
        int u=idx[i];
        for(rg int i=R.hd[u];i;i=R.e[i].nxt){
          int v=R.e[i].to;
          if(!dfn[v])continue;
          F(v);
          if(dfn[sdm[mn[v]]]<dfn[sdm[u]])sdm[u]=sdm[mn[v]];
        }
        D.ade(sdm[u],u),f[u]=fa[u];
        for(rg int i=D.hd[fa[u]];i;i=D.e[i].nxt){
          int v=D.e[i].to;F(v);//运用推论一求idom
          idm[v]=sdm[mn[v]]==fa[u]?fa[u]:mn[v];
        }
        D.hd[fa[u]]=0;
      }
      for(rg int i=2;i<=tim;i++){
        int u=idx[i];//完善推论一的第二种情况
        if(idm[u]!=sdm[u])idm[u]=idm[idm[u]];
      }
    }
    int siz[N];
    int main(){
      Read(n),Read(m);
      for(rg int i=1,u,v;i<=m;i++){
        Read(u),Read(v),G.ade(u,v),R.ade(v,u);
      }
      DFS(1,0),Tarjan();
      for(rg int i=tim;i;i--){
        int u=idx[i];//统计答案
        siz[idm[u]]+=++siz[u];
      }
      for(rg int i=1;i<=n;i++)printf("%d ",siz[i]);
      puts("");
      KafuuChino HotoKokoa
    }
    

    3.简单应用

    省选联考 2021 A 支配。
    考虑加入 ((u,v)) 之后满足哪些条件的点会发生变化(显然受支配集的变化一定是减少)。
    发生变化也就是某个祖先不再支配它,而一个点的受支配集变化会引起整个子树变化,所以我们只需要考虑父亲不再支配它的点。
    这样的点 (x) 一定满足 (1 ightsquigarrow u o v ightsquigarrow x) 且不经过 (x) 的父亲。
    显然是不能每次暴力求的,我们可以 (O(n^2)) 对于每个点预处理出不经过它的父亲就能到达它的所有点,复杂度已经足以通过本题。

    struct DominatorTree {
      struct Edge {
        int to,nxt;
      }e[N];
      int hd[N],cnt;
      il void ade(int u,int v){
        e[++cnt].to=v,e[cnt].nxt=hd[u],hd[u]=cnt;
      }
      int dfn[N],tim,siz[N];
      void DFS(int u,int ff){
        dfn[u]=++tim,siz[u]=1;
        for(rg int i=hd[u];i;i=e[i].nxt){
          int v=e[i].to;
          if(v!=ff)DFS(v,u),siz[u]+=siz[v];
        }
      }
    }T;
    bool vis[N];
    void DFSForbid(int u,int ban){
      vis[u]=1;
      for(rg int i=R.hd[u];i;i=R.e[i].nxt){
        int v=R.e[i].to;
        if(!vis[v]&&v!=ban)DFSForbid(v,ban);
      }
    }
    bool ok[N][N];//whether i can reach j without Xing fa[j]
    void Init(){
      for(rg int i=2;i<=n;i++){
        memset(vis,0,sizeof vis);
        DFSForbid(i,idm[i]);
        for(rg int j=1;j<=n;j++)ok[j][i]=vis[j];
      }
    }
    bool isf[N];int d[N];
    int Query(int u,int v){
      memset(isf,0,sizeof isf),memset(d,0,sizeof d);
      for(rg int i=u;i;i=idm[i])isf[i]=1;
      for(rg int i=2;i<=n;i++){
        if(!isf[idm[i]]&&ok[v][i]){
          d[T.dfn[i]]++,d[T.dfn[i]+T.siz[i]]--;
        }
      }
      int res=0;
      for(rg int i=1;i<=n;i++)res+=(d[i]+=d[i-1])!=0;
      return res;
    }
    int main(){
      Read(n),Read(m),Read(q);
      for(rg int i=1,u,v;i<=m;i++){
        Read(u),Read(v),G.ade(u,v),R.ade(v,u);
      }
      Tarjan();
      for(rg int i=2;i<=n;i++)T.ade(idm[i],i);
      Init(),T.DFS(1,0);
      for(rg int i=1,u,v;i<=q;i++){
        Read(u),Read(v);
        cout<<Query(u,v)<<endl;
      }
      KafuuChino HotoKokoa
    }
    

    可以使用 (O(n^2)) 暴力求支配树也可以使用 Tarjan 的算法,我为了练习选择了后者。然后就因为计算 ( ext{idom}) 时写反 (u,v) 调了一下午。

    内容来自_ajthreac_的博客(https://www.cnblogs.com/juruoajh/),未经允许,不得转载。
  • 相关阅读:
    烟大课表PC端-不仅仅是浏览器和手机APP
    关于51单片机电子时钟精度的问题
    第十二周项目4-点、圆的关系
    Git on Windows 一些问题
    vi 的使用
    Git 账户认证的一些问题
    [Windows] win7 配置Java开发环境
    Velocity 局部定制模板
    [Storm] Storm与asm的恩恩怨怨
    [Storm] No data flows into bolt
  • 原文地址:https://www.cnblogs.com/juruoajh/p/15240363.html
Copyright © 2011-2022 走看看