仙人掌&圓方樹學習筆記
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)
首先我們不考慮修改,再來想想這道題目。
我們既然要求的是最小值,那么,在經過一個點雙的時候,走的一定是具有較小權值的那一側。
所以說,我們可以讓所有的方點表示它所在的點雙的最小權值,
這樣子只需要對於圓方樹樹鏈剖分之后維護鏈的最小值就行了。
好的,回歸帶修改,無非是要動態的維護一下方點的最小權值了。
你問我怎么動態維護若干個值的最小值?搞個\(multiset\)不就好了嗎?
但是,現在問題又來了,如果每次修改一個點的權值(這個點當然是圓點啦),
那么,必定會修改所有和它相鄰的方點,如果是一個菊花樹,然后我們拼命修改根節點,這樣子復雜度就起飛了。
現在讓我們打開腦洞,大力思考一下怎么辦?
我們強行讓方點的權值不包括它的父親(也就是只算它的兒子)
如果求解的時候\(LCA\)是方點,則額外計算一下方點父親的權值
這樣子每個圓點在修改的之后只需要向上修改給父親方點啦!
於是,我們得到了\(Tarjan\)+圓方樹+樹鏈剖分+線段樹+\(multiset\)的\(O(nlog^2n)\)的做法啦
(為什么要手寫可刪堆啊?\(multiset\)不好嗎?)
8、完結撒花
呼,終於寫完啦。
最后再來總的說一說仙人掌和圓方樹。
對於仙人掌,我們對應的圓方樹唯一。
無論是廣義圓方樹還是普通的圓方樹,和普通的樹相比,唯一的區別就是要額外考慮方點的貢獻。
總的來說,就是碼農+思維題啦,只要想清楚方點的處理,一切都很好辦了。