日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【学习笔记】支配树

發(fā)布時(shí)間:2023/12/20 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【学习笔记】支配树 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 0.概述
  • 1.有關(guān) dfs\rm dfsdfs
    • 1.1.定義
    • 1.2.性質(zhì)
      • 1.2.1.非樹邊
      • 1.2.2.dfn\text{dfn}dfn 推論
  • 2.有關(guān)支配樹
    • 2.1.半支配點(diǎn)與 semisemisemi
      • 2.1.1.性質(zhì)
      • 2.1.2.求 semisemisemi
    • 2.2.支配點(diǎn)
    • 2.3.支配樹
      • 2.3.1.定義
      • 2.3.2.存在性
      • 2.3.3.建支配樹
    • 2.4.代碼實(shí)現(xiàn)
  • 3.例題
    • 3.1.板題
  • 4.后記

0.概述

用來(lái)求一張圖中,從某個(gè)定點(diǎn) sss 出發(fā),到達(dá)某一點(diǎn) eee 的路徑上,必須經(jīng)過(guò)哪些點(diǎn)。

這玩意兒有什么用呢?沒(méi)什么用。 我只是個(gè)普及組選手啊😓

1.有關(guān) dfs\rm dfsdfs

1.1.定義

在一個(gè)圖中,以某一點(diǎn)為起點(diǎn),進(jìn)行深度優(yōu)先遍歷,將所有走過(guò)的邊當(dāng)作樹邊,形成的 樹 。

注意:邊可能是有向的。所以這并非一個(gè)傳統(tǒng)意義上的樹。

1.2.性質(zhì)

dfn?(u)\operatorname{dfn}(u)dfn(u)uuu 是第幾個(gè)被訪問(wèn)的(回溯不計(jì)入訪問(wèn)次數(shù))。

1.2.1.非樹邊

在有向圖中,對(duì)于任意非樹邊 ?u,v?\langle u,v\rangle?u,v?,要么 uuuvvv 的祖先,要么 dfn(u)>dfn(v)\text{dfn}(u)>\text{dfn}(v)dfn(u)>dfn(v)

證明是輕松的。在 uuu 回溯時(shí),對(duì)于所有已經(jīng)訪問(wèn)的節(jié)點(diǎn) xxx,要么 dfn(x)<dfn(u)\text{dfn}(x)<\text{dfn}(u)dfn(x)<dfn(u),要么 xxxuuu 的子樹內(nèi)一點(diǎn)。而 vvv 都不滿足,則此時(shí)必將 dfs\rm dfsdfsvvv,進(jìn)而 (u,v)(u,v)(u,v) 為樹邊,出現(xiàn)矛盾。

1.2.2.dfn\text{dfn}dfn 推論

對(duì)于任意兩個(gè)點(diǎn) u,v(u≠v)u,v\;(u\ne v)u,v(u?=v),不妨設(shè) dfn(u)<dfn(v)\text{dfn}(u)<\text{dfn}(v)dfn(u)<dfn(v),記 lca(u,v)=xlca(u,v)=xlca(u,v)=x

  • 對(duì)于 vvvxxx 的樹上路徑中一點(diǎn) yyy,一定有 dfn(u)<dfn(y)\text{dfn}(u)<\text{dfn}(y)dfn(u)<dfn(y)
  • 對(duì)于 uuuxxx 的樹上路徑中一點(diǎn) yyy,一定有 dfn(y)<dfn(v)\text{dfn}(y)<\text{dfn}(v)dfn(y)<dfn(v)

其正確性是顯然的;只需要畫一張圖就可以領(lǐng)會(huì)其中真諦了。用大白話來(lái)說(shuō)就是,兩個(gè)點(diǎn)的 dfn\rm dfndfn 比較,在不超過(guò) lcalcalca 的位置都是一樣的。

2.有關(guān)支配樹

如果圖是無(wú)向圖,無(wú)向邊轉(zhuǎn)化為兩條有向邊。故以下內(nèi)容均討論有向圖。

為了表述方便,u?vu\Rightarrow vu?v 表示 uuu 通過(guò)一條原圖中存在的有向邊 ?u,v?\langle u,v\rangle?u,v? 到達(dá) vvv,即二者直接相連。而 u→vu\rightarrow vuv 表示 u???vu\Rightarrow \cdots \Rightarrow vu???vu?vu\Rightarrow vu?v

2.1.半支配點(diǎn)與 semisemisemi

對(duì)于一個(gè)點(diǎn) uuu,若存在一條路徑 v→u(v≠u)v\rightarrow u\;(v\ne u)vu(v?=u),使得路徑上除 uuuvvv 的點(diǎn) xxx 都滿足 dfn(x)>dfn(u)\text{dfn}(x)>\text{dfn}(u)dfn(x)>dfn(u),則 vvvuuu 的半支配點(diǎn)。

規(guī)定 semi(x)semi(x)semi(x)xxx 的所有半支配點(diǎn)中 dfn\text{dfn}dfn 最小的一個(gè)。

2.1.1.性質(zhì)

semi(x)semi(x)semi(x)xxxdfs\rm dfsdfs 樹上的祖先。我盡量用簡(jiǎn)潔的方式證明它。

首先,xxx 的父節(jié)點(diǎn)是 xxx 的半支配點(diǎn),故
dfn[semi(x)]≤dfn(fax)<dfn(x)\text{dfn}[semi(x)]\le\text{dfn}(fa_x)<\text{dfn}(x) dfn[semi(x)]dfn(fax?)<dfn(x)

這是比較核心的關(guān)系。有了這個(gè),考慮 semi(x)→xsemi(x)\rightarrow xsemi(x)x 路徑上經(jīng)過(guò)的第一個(gè)點(diǎn) yyy(即路徑為 semi(x)?y→xsemi(x)\Rightarrow y\rightarrow xsemi(x)?yx,顯然 yyy 是存在的,否則 semi(x)semi(x)semi(x) 已經(jīng)是 xxx 的祖先),顯然有 dfn(y)>dfn(x){\rm dfn}(y)>{\rm dfn}(x)dfn(y)>dfn(x)

仔細(xì)思考一下,dfn(y)>dfn(x)>dfn[semi(x)]{\rm dfn}(y)>{\rm dfn}(x)>{\rm dfn}[semi(x)]dfn(y)>dfn(x)>dfn[semi(x)],那么 semi(x)?ysemi(x)\Rightarrow ysemi(x)?y 應(yīng)當(dāng)為祖先到兒子的邊。即 semi(x)semi(x)semi(x)yyy 的祖先。

根據(jù) 1.2.2.dfn推論,semi(x)semi(x)semi(x) 至少要達(dá)到 lca(x,y)lca(x,y)lca(x,y) 才可以滿足 dfn[semi(x)]<dfn(x){\rm dfn}[semi(x)]<{\rm dfn}(x)dfn[semi(x)]<dfn(x),于是它就是 xxx 的祖先了。

2.1.2.求 semisemisemi

semi(x)semi(x)semi(x) 其實(shí)就是求半支配點(diǎn)。更新的時(shí)候,用 semisemisemi 更新(而非集合取并集)即可。

枚舉 semi(x)→xsemi(x)\rightarrow xsemi(x)xxxx 的前驅(qū) yyy,如果 dfn(y)<dfn(x){\rm dfn}(y)<{\rm dfn}(x)dfn(y)<dfn(x),那就只能直接用 yyy 更新 semi(x)semi(x)semi(x) 了。否則,假設(shè)第一次走到 lca(x,y)→ylca(x,y)\rightarrow ylca(x,y)y 的點(diǎn)為 zzz,顯然所有不到 lca(x,y)lca(x,y)lca(x,y)yyy 的祖先都可以作為 zzz,也都可以用 semi(z)semi(z)semi(z) 更新 semi(x)semi(x)semi(x)

形式化的,候選 semisemisemi 點(diǎn)的集合為 SxS_xSx?,那么有
Sx=??y,x??z∈path(y,root)dfn(z)>dfn(x)SzS_x=\bigcup_{\langle y,x\rangle}\bigcup_{z\in path(y,root)}^{{\rm dfn}(z)>{\rm dfn}(x)}S_z Sx?=?y,x???zpath(y,root)?dfn(z)>dfn(x)?Sz?

顯然 SzS_zSz? 是合法的,因?yàn)?semi(z)semi(z)semi(z)zzz 的路徑上的點(diǎn)的 dfn>dfn(z)>dfn(x){\rm dfn}>{\rm dfn}(z)>{\rm dfn}(x)dfn>dfn(z)>dfn(x) 。不過(guò)它是否有所遺漏呢?即 dfn\rm dfndfn 介于 dfn(x){\rm dfn}(x)dfn(x)dfn(z){\rm dfn}(z)dfn(z) 之間的點(diǎn)?

并沒(méi)有呢。那些點(diǎn)想要走到 zzz,那就是讓 dfn\rm dfndfn 增加,那么這樣的點(diǎn)必須是 zzz 的祖先。而 zzz 的祖先是不會(huì)先于 zzz 走到的!——比 lca(x,z)lca(x,z)lca(x,z) 更深的,根據(jù)定義,不應(yīng)該在 zzz 之前訪問(wèn);比那更淺的,不能出現(xiàn)在路徑上,因?yàn)槠?dfn\rm dfndfn 小于 dfn(x){\rm dfn}(x)dfn(x) 了。

2.2.支配點(diǎn)

在一張圖中設(shè)立一個(gè)源點(diǎn) sss

若對(duì)于某一點(diǎn) e(e≠s)e\;(e\ne s)e(e?=s) 滿足所有從 ssseee 的路徑中都經(jīng)過(guò) uuu(即,在圖中刪去 uuu 之后,不存在 ssseee 的路徑),則稱 uuueee 的支配點(diǎn),或 uuu 支配 eee

顯然一個(gè)點(diǎn)的支配點(diǎn)可能有很多個(gè)。一個(gè)生動(dòng)形象的例子是鏈。

2.3.支配樹

2.3.1.定義

對(duì)于一張圖和其源點(diǎn) sss,支配樹是滿足該條件的樹:

  • 包含圖中的每一個(gè)節(jié)點(diǎn)。
  • 對(duì)于任意一點(diǎn) u(u≠s)u\;(u\ne s)u(u?=s),其祖先(包括 uuu 本身)均為其支配點(diǎn)。
  • 對(duì)于任意一點(diǎn) u(u≠s)u\;(u\ne s)u(u?=s),其支配點(diǎn)均為其祖先(包括 uuu 本身)。

sss 會(huì)是樹的根節(jié)點(diǎn)。

2.3.2.存在性

我們要說(shuō)明,為什么它會(huì)是一顆樹?就像 SAM\tt SAMSAM 要說(shuō)明,為什么存在合法的自動(dòng)機(jī)狀態(tài)轉(zhuǎn)移。

先證明一個(gè)小結(jié)論:若 vvvuuu 的每一條路徑都經(jīng)過(guò) xxx,則存在一條從 xxxuuu 的路徑不含 vvv

證明很簡(jiǎn)單。用一點(diǎn) C++\text{C++}C++ 的想法。反證法,若不成立,其函數(shù)應(yīng)該像這樣:

void x_goto_u(){x_goto_v(); // 由假設(shè),必須先到達(dá)vv_goto_u(); // 調(diào)用下面的函數(shù) } void v_goto_u(){v_goto_x(); // 由假設(shè),必須先到達(dá)xx_goto_u(); // 調(diào)用上面的函數(shù) }

這樣就死遞歸了呀!而路徑是客觀存在的,所以歸謬了。類似的,兩個(gè)點(diǎn)互相支配也是不可能的。

現(xiàn)在,我們可以說(shuō)明一些支配點(diǎn)之間的性質(zhì)。若 uuu 有兩個(gè)支配點(diǎn) vvvxxx,但是 vvvxxx 沒(méi)有支配關(guān)系(即支配關(guān)系非 “樹形”),則存在兩條從 sssuuu 的路徑,一個(gè)是 s→v→us\rightarrow v\rightarrow usvu,另一個(gè)是 s→x→us\rightarrow x\rightarrow usxu

對(duì)于前者,令 s→vs\rightarrow vsv 不經(jīng)過(guò) xxx(由于 vvvxxx 沒(méi)有支配關(guān)系,該路徑必然存在)。為了使 xxx 支配 uuu,必須讓 v→uv\rightarrow uvu 總是經(jīng)過(guò) xxx 。根據(jù)上面的結(jié)論,x→ux\rightarrow uxu 可以不經(jīng)過(guò) vvv,同理,s→xs\rightarrow xsx 可以不經(jīng)過(guò) vvv,于是 s→x→us\rightarrow x\rightarrow usxu 完全不會(huì)經(jīng)過(guò) vvv,說(shuō)明 vvv 不支配 uuu 啦,矛盾!

有了這一條,你會(huì)發(fā)現(xiàn),一個(gè)點(diǎn)的所有支配點(diǎn)的導(dǎo)出子圖是完全圖。這個(gè)完全圖又不能存在有向環(huán)(否則存在兩個(gè)點(diǎn)互相支配,這是荒謬的),所以存在一個(gè)點(diǎn)被其他所有點(diǎn)支配。那么當(dāng)前點(diǎn)就以這個(gè)點(diǎn)作為支配樹上的父節(jié)點(diǎn)即可。

有點(diǎn)歸納法的感覺(jué),對(duì)嗎?對(duì)。按照 支配關(guān)系有向圖 的拓?fù)湫蚪浼纯伞?/p>

2.3.3.建支配樹

據(jù)說(shuō)又是 tarjantarjantarjan 發(fā)明的算法……

我們要 semisemisemi 有什么用? 去掉所有非樹邊,連邊 ?semi(x),x?\langle semi(x),x\rangle?semi(x),x?,支配關(guān)系不改變

其實(shí)這個(gè)原因挺簡(jiǎn)單的。理解一下 semisemisemi 到底是什么:越過(guò)樹邊上的點(diǎn)。如果 uuuvvv 的祖先,并且 u→vu\rightarrow vuv,路徑上 不需要dfn<dfn(v){\rm dfn}<{\rm dfn}(v)dfn<dfn(v) 的點(diǎn)——如果有,那其實(shí)還是 vvv 的祖先,沒(méi)有飛越樹上的點(diǎn)。

支配點(diǎn)肯定是 dfs\rm dfsdfs 樹上的祖先(否則樹邊構(gòu)成一條路徑)。其樹上祖先,如果不是支配點(diǎn),只可能是被跨過(guò)了。而跨過(guò)就是 semisemisemi 嘛。如果感性的理解一下,semisemisemi 已經(jīng)建好了所有橋,不需要更多的邊了。

這樣有什么好處呢?至少有一個(gè):原圖變?yōu)榱?/strong> DAGDAGDAG

其實(shí) DAGDAGDAG 已經(jīng)可以搞定了。對(duì)于所有前驅(qū),求支配點(diǎn)交集,在支配樹上體現(xiàn)為 lcalcalca 的祖先。按照 dfn\rm dfndfn 排序后,使用帶權(quán)并查集,倍增維護(hù) lcalcalca 。時(shí)間復(fù)雜度 O(nlog?n)O(n\log n)O(nlogn)

可是人總是精益求精的。令 idom(x)idom(x)idom(x)xxx 的支配點(diǎn)中 dfn\text{dfn}dfn 最大的點(diǎn),尋找 idom(x)idom(x)idom(x) 的方法是:

PPP 為從 semi(x)semi(x)semi(x)xxx 的樹上路徑點(diǎn)集(不包括 semi(x)semi(x)semi(x) 本身),找一個(gè) z∈Pz\in PzP 滿足 dfn[semi(z)]\text{dfn}[semi(z)]dfn[semi(z)] 最小。

semi(z)=semi(x)semi(z)=semi(x)semi(z)=semi(x),則有 idom(x)=semi(x)idom(x)=semi(x)idom(x)=semi(x);否則有 idom(x)=idom(z)idom(x)=idom(z)idom(x)=idom(z)

對(duì)于前半句性感的證明就是,沒(méi)有 semi(x)semi(x)semi(x) 的祖先連到 PPP 中的邊,則刪去 semi(x)semi(x)semi(x)xxx 就不可達(dá)。畢竟 PPP 以內(nèi)的所有點(diǎn)都連接 ?semi(p),p?\langle semi(p),p\rangle?semi(p),p? 之后也不能跨過(guò) semi(x)semi(x)semi(x) 。別忘了 zzz 的定義哦!

對(duì)于后半句性感的證明(見下圖)就是:

假設(shè)刪掉 idom(z)idom(z)idom(z)xxx 依舊可達(dá),則說(shuō)明在 dfsdfsdfs 樹上,idom(z)idom(z)idom(z) 有一個(gè)祖先,可以走一條非樹邊(也就是通過(guò) semisemisemi 連出來(lái)的邊,圖中紅邊)到達(dá) xxxidom(z)idom(z)idom(z) 中間的一個(gè)點(diǎn) kkk

zzz 不是 kkk 的祖先,則刪掉 idom(z)idom(z)idom(z)zzz 仍可達(dá),與支配點(diǎn)定義不符,所以 zzzkkk 的祖先。那么因?yàn)?z∈Pz\in PzP ,所以 k∈Pk\in PkP 。因?yàn)閯h除 idom(z)idom(z)idom(z)semi(z)semi(z)semi(z) 不可達(dá),所以 dfn[semi(k)]<dfn[idom(z)]<dfn[semi(z)]\text{dfn}[semi(k)]<\text{dfn}[idom(z)]<\text{dfn}[semi(z)]dfn[semi(k)]<dfn[idom(z)]<dfn[semi(z)],與 zzz 的定義矛盾,所以該假設(shè)不可能成立。


版權(quán)聲明:本文為CSDN博主「litble」的原創(chuàng)文章,遵循CC 4.0 by-sa版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/litble/article/details/83019578

這一小節(jié)是摘錄的,進(jìn)行了部分修改。

2.4.代碼實(shí)現(xiàn)

我們要 idomidomidom 來(lái)干嘛呢?在支配樹上,xxx 的父親就是 idom(x)idom(x)idom(x) 唄。

發(fā)現(xiàn) semisemisemi 需要用到 dfn\text{dfn}dfn 值更大的點(diǎn)的 semisemisemi 。那就按照 dfn\text{dfn}dfn 從大到小處理。

帶權(quán)并查集 好像可以做。用 mi(x)mi(x)mi(x) 存該點(diǎn)到根節(jié)點(diǎn)的路徑中,dfn[semi(z)]\text{dfn}[semi(z)]dfn[semi(z)] 值最小的一個(gè) zzz 。畢竟也就用 yyy 的祖先中比 xxxdfn\text{dfn}dfn 大的點(diǎn)唄!

idomidomidom 該怎么辦呢?它要用到 semi(x)semi(x)semi(x)xxx 之間的信息。那就等到 semi(x)semi(x)semi(x) 處理完的時(shí)候處理吧!——那個(gè)時(shí)候,中間的所有點(diǎn)都處理了。

很巧的是,恰好此時(shí) semi(x)semi(x)semi(x) 的祖先還沒(méi)處理!可以一并使用并查集,因?yàn)楦?jié)點(diǎn)就是 semi(x)semi(x)semi(x)

3.例題

3.1.板題

傳送門 to HDU

#include<bits/stdc++.h> using namespace std; #define RI register int int read() {int q=0;char ch=' ';while(ch<'0'||ch>'9') ch=getchar();while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();return q; } typedef long long LL; const int N=50005,M=100005; int n,m,tim; int dfn[N],repos[N],mi[N],fa[N],f[N],semi[N],idom[N],ans[N]; struct graph{int tot,h[N],ne[M],to[M];void clear() {tot=0;for(RI i=0;i<=n;++i) h[i]=0;}//此題數(shù)據(jù)有誤所以應(yīng)從i=0開始清空void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;} }g,rg,ng,tr;void init() {tim=0;g.clear(),rg.clear(),ng.clear(),tr.clear();for(RI i=1;i<=n;++i)repos[i]=dfn[i]=idom[i]=fa[i]=ans[i]=0,mi[i]=semi[i]=f[i]=i; } void tarjan(int x) {dfn[x]=++tim,repos[tim]=x;for(RI i=g.h[x];i;i=g.ne[i])if(!dfn[g.to[i]]) fa[g.to[i]]=x,tarjan(g.to[i]); } int find(int x) {if(x==f[x]) return x;int tmp=f[x];f[x]=find(f[x]);if(dfn[semi[mi[tmp]]]<dfn[semi[mi[x]]]) mi[x]=mi[tmp];return f[x]; } void dfs(int x,LL num) {ans[x]=num+x;for(RI i=tr.h[x];i;i=tr.ne[i]) dfs(tr.to[i],num+x); } void work() {for(RI i=n;i>=2;--i) {int x=repos[i],tmp=n;for(RI j=rg.h[x];j;j=rg.ne[j]) {if(!dfn[rg.to[j]]) continue;//此題數(shù)據(jù)有誤if(dfn[rg.to[j]]<dfn[x]) tmp=min(tmp,dfn[rg.to[j]]);else find(rg.to[j]),tmp=min(tmp,dfn[semi[mi[rg.to[j]]]]);}semi[x]=repos[tmp],f[x]=fa[x],ng.add(semi[x],x);x=repos[i-1];for(RI j=ng.h[x];j;j=ng.ne[j]) {int y=ng.to[j];find(y);if(semi[mi[y]]==semi[y]) idom[y]=semi[y];else idom[y]=mi[y];//此時(shí)idom[mi[y]]可能并未找到}}for(RI i=2;i<=n;++i) {int x=repos[i];if(idom[x]!=semi[x]) idom[x]=idom[idom[x]];tr.add(idom[x],x);}dfs(n,0); } int main() {int x,y;while(~scanf("%d%d",&n,&m)) {init();for(RI i=1;i<=m;++i)x=read(),y=read(),g.add(x,y),rg.add(y,x);tarjan(n);work();for(RI i=1;i<n;++i) printf("%d ",ans[i]);printf("%d\n",ans[n]);}return 0; }

4.后記

再次鳴謝:litble的感性理解支配樹,提供了思路與代碼!

支配樹為作者自學(xué),如有錯(cuò)誤之處,還請(qǐng)大佬斧正!

總結(jié)

以上是生活随笔為你收集整理的【学习笔记】支配树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。