從縮點到圓方樹

2022-05-27 21:54:17 字數 3090 閱讀 1012

連通:無向圖中的任意兩點都可以互相到達。

強連通:有向圖中的任意兩點都可以互相到達。

連通分量:無向圖的極大連通子圖。

強連通分量:有向圖的極大強連通子圖。

dfs 生成樹:對一張圖(有向無向均可)進行深度優先遍歷得到的生成樹。

樹邊:在 dfs 生成樹上的邊。

前向邊:由子樹的根連向子樹內的非樹邊。

返祖邊:由結點連向其祖先的邊。

橫叉邊:除上面三種之外的邊。

割點:無向連通圖刪除某個點及其所有連邊後,圖不再聯通,則該點是圖的乙個割點。

割點(橋):無向連通圖刪除某條邊後,圖不再聯通,則該邊是圖的一條割邊。

點雙聯通圖:不存在割點的無向聯通圖。

邊雙聯通圖:不存在割邊的無向聯通圖。

點雙聯通分量:無向連通圖的極大點雙聯通子圖。

邊雙聯通分量:無向連通圖的極大邊雙聯通子圖。

對於結點 \(u\),記錄兩個資訊 \(dfn_u\) 和 \(low_u\)。

\(dfn\) 表示時間戳,即第幾個被遍歷到的點。

\(low\) 表示從當前點開始,經過的邊的兩個端點均處在未找出的強連通分量中,能到達的最小的時間戳。

在 dfs 的過程中,將經過的點塞進乙個棧裡面。一旦發現 \(dfn_u=low_u\) 就一直彈棧直至彈出結點 \(u\),彈出的這些點就構成了乙個強連通分量。

然後考慮如何求出 \(low_u\),列舉 \(u\) 的每條出邊 \((u,v)\)。

\(low\) 陣列其實是在找一條向上(\(dfn\) 遞減)的路徑,而兩個強連通分量是不可能有公共點的,所以我們才會有經過邊的限制。

就像這樣,\(1\to 2\to 3\to 4\),\(4\) 經過紅色的橫叉邊到了 \(dfn\) 更小的點 \(3\) 上,而 \(3\) 又可以合併到 \(2\) 上,\(4\) 又肯定是從 \(2\) 下來的,所以 \(4\) 屬於 \(2\) 的強連通分量。

但是還有乙個問題,\(low\) 陣列有時會不能更新完全,怎麼辦呢?

按照 \(1\to 2\to 3\) 的順序走,假設先遍歷到了綠色邊,再遍歷到了紅色邊,可以發現,\(low_3\) 沒有更新完全的原因是 \(low_2\) 沒有更新完全。所以問題出在已遍歷過的情況中。

但其實是沒有關係的,\(low\) 陣列的目的僅僅是判斷當前強連通分量是否能夠向上合併。就算沒有更新完全,如果能夠向上合併的話,\(dfn_\) 肯定還是小於 \(dfn_u\) 的。

所以可以將取 \(\min\) 中的 \(low_v\) 換成 \(dfn_v\)。

那麼演算法的正確性就很顯然了,在合法的情況下(能夠向上合併)盡可能將當前強連通分量擴大。

可以發現對無向圖不可能存在橫叉邊。並且樹邊和前向邊都是返祖邊,返祖邊僅由它們兩種邊組成。

隨便欽定乙個根節點,判斷它是否為割點很簡單:是否存在兩棵及以上的子樹。

誒那對每個點都這麼做一下不就好了?對不起,每次需要遍歷子樹,兩個兒子可能是連一起的。

同樣地,\(dfn\) 表示時間戳。但 \(low\) 的定義要換一下,改成最多經過一條非樹邊或反向經過一條樹邊,能到達的最小的時間戳。

這麼說也許不是很準確,其實就是子樹中的點能直接到達的子樹外的點的最小時間戳。

因為子樹內部可以隨便走來走去。

如果存在 \((u,v)\) 其中 \(u\) 是 \(v\) 的父親使得 \(low_v=low_u\) 則說明以 \(v\) 為根子樹中的點如果不通過 \(u\) 就一定不能向上走了,說明 \(u\) 是割點。

如果點 \(u\) 的所有樹邊都不能說明 \(u\) 是割點,那麼 \(u\)一定不是割點。因為子樹內所有的點都能走到子樹內某些點,然後通過條邊連出去。

所以我們僅僅需要將所有邊判斷一下。同樣地,這裡的更新咋寫?

樹邊 \(low_u=\min\\)?對。

非樹邊 \(low_u=\min\\)?錯!

請注意這裡是割,乙個點被割掉後包含該點的所有邊都被刪掉了!如果我們恰好經過一條非樹邊或反向經過一條樹邊回到了某個割點,然後再經過一條非樹邊或反向經過一條樹邊回到更上面的點……顯然是不合法的!

所以我們僅能經過條非樹邊或反向經過一條樹邊回上去(把 \(low_v\) 改成 \(dfn_v\)),這樣就算回到了割點,也不能向上跳了,並不會導致割點的判斷錯誤。而多於一條邊就可能出錯了!

顯然這樣 \(low\) 的更新是完全的。

\(low\) 的定義和割點稍有不同,不能反向經過樹邊

不取等於是因為 \(u\) 在子樹外。其餘原理分析類似,不贅述。

唯一需要注意的是反向經過一條樹邊的處理。當出現重邊時,我們會誤判。所以當第二次遇到回到父親的邊時,直接將父親的 \(dfn\) 賦給當前的 \(low\) 即可。

先講這個是因為比較簡單。

找出所有的割邊並割掉,剩下的連通塊就是所有的邊雙聯通分量。

其實就是每次刪除割點,分成若干個不連通的子圖,然後將割點重新加入每個子圖,但不合併子圖。如果找不到割點就說明是點雙聯通分量了。

當然不能直接這麼幹。在找割點的過程中,將經過的點塞進乙個棧裡面,一旦發現點 \(u\) 的兒子點 \(v\) 滿足 \(low_v=low_u\) 就不斷彈棧直到彈出點 \(v\),再加上點 \(u\) 就是乙個點雙聯通分量。

乙個割點至少存在於兩個點雙聯通分量中。

原圖中每個點雙都對應乙個方點,每個點都對應乙個圓點,每個方點向其點雙中的點對應的圓點連邊。

比如說菊花圖和鏈的圓方樹分別長這樣:

一些性質:

學習筆記 圓方樹

圓方樹 元芳你怎麼看 圓方樹推薦 圓方樹是什麼?tarjan家族中,最不好處理的是點雙 因為乙個割點可能屬於很多的dcc。為了把圖縮成一棵樹,我們不得不做出這樣的處理 摘自 這個樹不但醜。而且,對於乙個點雙內部的資訊,我們把它縮成了乙個點,內部的與非割點有關的路徑我們一無所知。所以這個玩意只能用來維...

圓方樹學習筆記

聽說g2的選手都會圓方樹了,所以我來學習一波。這個階段的學習我也僅僅只是知道了圓方樹是什麼,比較簡單的題目怎麼寫,但是對於一些思維難度較高,實現能力要求較高的題目,我還不是很會.希望未來能越來越好 點雙連通分量,仙人掌的定義。點分為圓點和方點的樹.把仙人掌上的點保留,然後如果存在乙個點雙就新建乙個方...

圓方樹學習筆記

寫這個東西只是記錄一下我學過圓方樹 text 圓方樹是一種將圖變成樹的方法。首先,把原圖中的所有點都看成圓點,我們需要求出圖中所有的點雙連通分量,可以使用 tarjan 演算法。然後,在每乙個點雙連通分量中間建立乙個方點,將此點雙連通分量中的所有點向這個方點連邊。這樣就可以把圖轉成樹,利用一些樹上的...