1. 程式人生 > >仙人掌&圓方樹學習筆記

仙人掌&圓方樹學習筆記

https 思維題 bzoj 不變 n) 條件 連通性 普通 一朵

仙人掌&圓方樹學習筆記

1、仙人掌

圓方樹用來幹啥?

——處理仙人掌的問題。

仙人掌是啥?

技術分享圖片

(圖片來自於\(BZOJ1023\))

——也就是任意一條邊只會出現在一個環裏面。

當然,如果你的圖片想看起來舒服一點,也可以把圖片變成這樣子

技術分享圖片

(圖片來源於網絡)

2、DFS樹

為啥要寫這個?--因為這個看起來也可以解決一些仙人掌的問題。

對於一個仙人掌,我們隨便構建出一棵生成樹。

然後我們就多了一些邊——可以叫返祖邊,非樹邊……你想叫啥就叫啥。

因為每條邊只會出現在一個環中,

所以每一條返祖邊覆蓋了樹中的一條鏈,這條鏈+這條邊構成了環。

所以我們可以確定每條邊都出現在了哪個環中。

這樣子可以解決一點點仙人掌的問題。

比如仙人掌的最大獨立集,\(dp\)的時候額外記錄一下所在環的返祖邊的端點的狀態就好了。

3、圓方樹是啥

看WC2017的營員交流的課件真的看得想死

先來定義一下什麽是圓方樹。

以下內容來自於課件:

仙人掌 \(G = (V, E)\) 的圓方樹 \(T = (V_T , E_T )\) 為滿足以下條件的無向圖:
\(V_ T = R_ T ∪ S_ T , R_ T = V, R_ T ∩ S_ T = ?\),我們稱$ R_ T$ 集合為圓點、\(S_ T\)
集合為方點
\(?e ∈ E\),若 \(e\) 不在任何簡單環中,則 \(e ∈ E_T\)
對於每個仙人掌中的簡單環 \(R\)

,存在方點 \(p_ R ∈ S_ T\) ,並且 \(?p ∈ R\) 滿
\((p _R , p) ∈ E_ T\) ,即對每個環建方點連所有點

聽說你看完挺混亂?

我來翻譯一下:

對於一個仙人掌,它的圓方樹如下定義:

首先分為了兩類點,一類是圓點,一類是方點。

圓點就是原仙人掌中所有的點,方點是我們新添加進去的點。

而圓方樹的連邊規則是這樣的:

如果一條邊在仙人掌中不屬於任何一個環中,那麽它直接圓方樹中的兩個圓點。

對於仙人掌中的任意一個環,則每個環上的點在圓方樹上對應的圓點向這個環對應的方點連邊。

如何證明圓方樹是一棵樹?

(以下內容來自於課件)

不在環上的邊在圓方樹中依然存在,

因此這些邊連通性不變;

每個環通過新建方點的方式連成一朵菊花,連通性不變。

因此圓方樹是無向連通圖。

原圖中環的個數為 \(|E| ? |V| + 1\),則
\(|V _T | = |S _T | + |R_ T | = |V| + |E| ? |V| + 1 = |E| + 1,|E _T | = |E|\)

(大小為\(r\) 的環在仙人掌和圓方樹中都是 \(r\) 條邊),因此滿足 \(|V_ T | = |E_ T | + 1\)

證明分為了兩步:

首先證明它是聯通的。

然後就證明了點數=邊數+1

這樣就是一棵樹了。

然後讓我把課件上的一張圖片給蒯過來

技術分享圖片

好的,圓方樹就長成這個樣子啦。

4、如何構建圓方樹

我想,看了上面的東西,知道了圓方樹是啥,我們很容易就知道怎麽構建圓方樹了吧。

首先\(Tarjan\)縮點,把每個大小超過\(1\)的環裏面的所有點都向一個新點(方點)連邊。

然後把多出來的鏈接兩個圓點的邊直接給連上就好了。

似乎真的很簡單?

5、圓方樹的性質

1.方點不會直接和方點相連

證明:

方點只會和屬於一個強連通分量的點相連,顯然不會和方點相連。

2.無論取哪個點為根,圓方樹的形態是一樣的

證明:

這不還是廢話嗎?

對於一個環,我們顯然對應的是一個方點,無論以哪個點為根,

這個環是不會變的,除了方點的編號不一樣之外就沒有任何不同了。

所以圓方樹是無根樹。

定義:子仙人掌

\(r\)為根的仙人掌上的點\(p\)的子仙人掌是去除掉\(p\)\(r\)的所有簡單路徑後,\(p\)所在的聯通塊

3.以\(r\)為根的仙人掌上\(p\)的子仙人掌就是圓方樹中以\(r\)為根時,\(p\)子樹中的所有圓點

證明:

如果\(p\)不在環上,顯然成立。

否則,此時去除所有到達根節點的所有簡單路徑後,

剩下的只有和\(p\)相連的,並且不和\(p\)在一個環內的點

顯然是圓方樹上\(p\)子樹中的圓點(和它在一個環內的圓點都不和它直接相連了)

6、如何解決仙人掌上的一些簡單的問題

a.求仙人掌的最大獨立集(BZOJ4316)

這道題目顯然有直接用\(dfs\)樹的\(dp\)求法,見這裏。

但是我們是在學習仙人掌,所以當然要用仙人掌的方法來求解啊。

其實這裏圓方樹沒有必要出來,沒有必要區分圓點和方點。

我們也是直接做\(dp\),但是與\(dfs\)樹不同的是,

我們直接在\(Tarjan\)過程中做\(dp\)(似乎本質也是\(dfs\)樹?)

碰到圓圓邊(圓點和圓點直接的邊)就是普通的樹型\(dp\)進行轉移

如果是一個環上的邊的話,那麽我們先暫時不進行操作。

當回到這個環的最上方的時候,對於這個環就行一次單獨的\(dp\)

將答案累加給環的頂端,這樣向上轉移又和樹一樣了

具體的題解戳這裏

b.求仙人掌直徑(BZOJ1023)

和普通的樹求直徑用一樣的\(dp\)就可以了。

對於一個環,拉出來特殊考慮,用一個單調隊列維護一下。

具體的實現方法戳這裏

c.仙人掌兩點間的最短路(BZOJ2125)

構建出圓方樹(圓方樹:終於需要用到我了),直接把圓方樹樹鏈剖分(主要是用來求\(LCA\))

圓圓邊和原仙人掌是同構的,圓方邊的權值定義如下:

我們知道方點的父親一定是圓點(轉換為有根樹之後),

這樣子把每條圓方邊的權值賦為到達方點父親的最短路徑就好啦。

更加詳細的題解和代碼戳這裏。

這一部分的小節

前兩個問題似乎用不到圓方樹,只需要普通的\(dp\)即可解決。實現的方法和普通的樹型\(dp\)是類似的,但是也有幾點區別:首先不是普通的\(dfs\),而是\(Tarjan\)算法的實現過程中,順帶解決\(dp\)問題。另外一個是仙人掌上的問題需要額外處理環的問題,每次找到環之後需要特殊處理。所以,解決這類問題也就是兩步:首先想好怎麽在樹上解決(圓圓邊如何解決),然後想好怎麽處理環的答案。

第三個問題就需要用到圓方樹啦,然而本質仍然是處理環和普通的樹邊之間的關系,只需要把這層關系想清楚,這一類問題應該還是比較好解決的。

7、廣義圓方樹

前面的圓方樹只能解決仙人掌的問題。

那麽,對於一個一般的無向圖,我們顯然也是可以利用圓方樹來解決的(要不然我寫什麽廣義圓方樹啊?)

我們在仙人掌中是對於每個點雙構建一個方點,在一般圖中我們也這麽做。

然後方點向所有點雙中的點連邊,差不多和仙人掌上的圓方樹是一樣的啦。

當然,和仙人掌上的圓方樹是有區別的啦,仙人掌上的圓方樹是圓圓點之間是可能有邊的。

那麽,廣義圓方樹呢?

先蒯張圖過來(來自\(ppl\)\(blog\))

技術分享圖片

發現了啥?

圓點和方點是相間的,怎麽搞?——強制把兩個點也看成一個點雙就好了

先找到題目來吧

帶修改,求無向圖中兩點之間所有簡單路徑上的最小權值(CF487E)

當然了,這道題目有翻譯好的版本UOJ#30,洛谷

首先我們不考慮修改,再來想想這道題目。

我們既然要求的是最小值,那麽,在經過一個點雙的時候,走的一定是具有較小權值的那一側。

所以說,我們可以讓所有的方點表示它所在的點雙的最小權值,

這樣子只需要對於圓方樹樹鏈剖分之後維護鏈的最小值就行了。

好的,回歸帶修改,無非是要動態的維護一下方點的最小權值了。

你問我怎麽動態維護若幹個值的最小值?搞個\(multiset\)不就好了嗎?

但是,現在問題又來了,如果每次修改一個點的權值(這個點當然是圓點啦),

那麽,必定會修改所有和它相鄰的方點,如果是一個菊花樹,然後我們拼命修改根節點,這樣子復雜度就起飛了。

現在讓我們打開腦洞,大力思考一下怎麽辦?

我們強行讓方點的權值不包括它的父親(也就是只算它的兒子)

如果求解的時候\(LCA\)是方點,則額外計算一下方點父親的權值

這樣子每個圓點在修改的之後只需要向上修改給父親方點啦!

於是,我們得到了\(Tarjan\)+圓方樹+樹鏈剖分+線段樹+\(multiset\)\(O(nlog^2n)\)的做法啦

(為什麽要手寫可刪堆啊?\(multiset\)不好嗎?)

代碼和題解

8、完結撒花

呼,終於寫完啦。

最後再來總的說一說仙人掌和圓方樹。

對於仙人掌,我們對應的圓方樹唯一。

無論是廣義圓方樹還是普通的圓方樹,和普通的樹相比,唯一的區別就是要額外考慮方點的貢獻。

總的來說,就是碼農+思維題啦。

仙人掌&圓方樹學習筆記