圓方樹用來幹啥?php
——處理仙人掌的問題。html
仙人掌是啥?算法
(圖片來自於\(BZOJ1023\))網絡
——也就是任意一條邊只會出如今一個環裏面。學習
固然,若是你的圖片想看起來舒服一點,也能夠把圖片變成這樣子spa
(圖片來源於網絡)翻譯
爲啥要寫這個?--由於這個看起來也能夠解決一些仙人掌的問題。3d
對於一個仙人掌,咱們隨便構建出一棵生成樹。code
而後咱們就多了一些邊——能夠叫返祖邊,非樹邊……你想叫啥就叫啥。htm
由於每條邊只會出如今一個環中,
因此每一條返祖邊覆蓋了樹中的一條鏈,這條鏈+這條邊構成了環。
因此咱們能夠肯定每條邊都出如今了哪一個環中。
這樣子能夠解決一點點仙人掌的問題。
好比仙人掌的最大獨立集,\(dp\)的時候額外記錄一下所在環的返祖邊的端點的狀態就行了。
看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
這樣就是一棵樹了。
而後讓我把課件上的一張圖片給蒯過來
好的,圓方樹就長成這個樣子啦。
我想,看了上面的東西,知道了圓方樹是啥,咱們很容易就知道怎麼構建圓方樹了吧。
首先\(Tarjan\)縮點,把每一個大小超過\(1\)的環裏面的全部點都向一個新點(方點)連邊。
而後把多出來的連接兩個圓點的邊直接給連上就行了。
彷佛真的很簡單?
1.方點不會直接和方點相連
證實:
方點只會和屬於一個強連通份量的點相連,顯然不會和方點相連。
2.不管取哪一個點爲根,圓方樹的形態是同樣的
證實:
這不仍是廢話嗎?
對於一個環,咱們顯然對應的是一個方點,不管以哪一個點爲根,
這個環是不會變的,除了方點的編號不同以外就沒有任何不一樣了。
因此圓方樹是無根樹。
定義:子仙人掌
以\(r\)爲根的仙人掌上的點\(p\)的子仙人掌是去除掉\(p\)到\(r\)的全部簡單路徑後,\(p\)所在的聯通塊
3.以\(r\)爲根的仙人掌上\(p\)的子仙人掌就是圓方樹中以\(r\)爲根時,\(p\)子樹中的全部圓點
證實:
若是\(p\)不在環上,顯然成立。
不然,此時去除全部到達根節點的全部簡單路徑後,
剩下的只有和\(p\)相連的,而且不和\(p\)在一個環內的點
顯然是圓方樹上\(p\)子樹中的圓點(和它在一個環內的圓點都不和它直接相連了)
這道題目顯然有直接用\(dfs\)樹的\(dp\)求法,見這裏。
可是咱們是在學習仙人掌,因此固然要用仙人掌的方法來求解啊。
其實這裏圓方樹沒有必要出來,沒有必要區分圓點和方點。
咱們也是直接作\(dp\),可是與\(dfs\)樹不一樣的是,
咱們直接在\(Tarjan\)過程當中作\(dp\)(彷佛本質也是\(dfs\)樹?)
碰到圓圓邊(圓點和圓點直接的邊)就是普通的樹型\(dp\)進行轉移
若是是一個環上的邊的話,那麼咱們先暫時不進行操做。
當回到這個環的最上方的時候,對於這個環就行一次單獨的\(dp\)
將答案累加給環的頂端,這樣向上轉移又和樹同樣了
和普通的樹求直徑用同樣的\(dp\)就能夠了。
對於一個環,拉出來特殊考慮,用一個單調隊列維護一下。
具體的實現方法戳這裏
構建出圓方樹(圓方樹:終於須要用到我了),直接把圓方樹樹鏈剖分(主要是用來求\(LCA\))
圓圓邊和原仙人掌是同構的,圓方邊的權值定義以下:
咱們知道方點的父親必定是圓點(轉換爲有根樹以後),
這樣子把每條圓方邊的權值賦爲到達方點父親的最短路徑就好啦。
更加詳細的題解和代碼戳這裏。
前兩個問題彷佛用不到圓方樹,只須要普通的\(dp\)便可解決。實現的方法和普通的樹型\(dp\)是相似的,可是也有幾點區別:首先不是普通的\(dfs\),而是\(Tarjan\)算法的實現過程當中,順帶解決\(dp\)問題。另一個是仙人掌上的問題須要額外處理環的問題,每次找到環以後須要特殊處理。因此,解決這類問題也就是兩步:首先想好怎麼在樹上解決(圓圓邊如何解決),而後想好怎麼處理環的答案。
第三個問題就須要用到圓方樹啦,然而本質仍然是處理環和普通的樹邊之間的關係,只須要把這層關係想清楚,這一類問題應該仍是比較好解決的。
前面的圓方樹只能解決仙人掌的問題。
那麼,對於一個通常的無向圖,咱們顯然也是能夠利用圓方樹來解決的(要否則我寫什麼廣義圓方樹啊?)
咱們在仙人掌中是對於每一個點雙構建一個方點,在通常圖中咱們也這麼作。
而後方點向全部點雙中的點連邊,差很少和仙人掌上的圓方樹是同樣的啦。
固然,和仙人掌上的圓方樹是有區別的啦,仙人掌上的圓方樹是圓圓點之間是可能有邊的。
那麼,廣義圓方樹呢?
先蒯張圖過來(來自\(ppl\)的\(blog\))
發現了啥?
圓點和方點是相間的,怎麼搞?——強制把兩個點也當作一個點雙就行了
先找到題目來吧
首先咱們不考慮修改,再來想一想這道題目。
咱們既然要求的是最小值,那麼,在通過一個點雙的時候,走的必定是具備較小權值的那一側。
因此說,咱們可讓全部的方點表示它所在的點雙的最小權值,
這樣子只須要對於圓方樹樹鏈剖分以後維護鏈的最小值就好了。
好的,迴歸帶修改,無非是要動態的維護一下方點的最小權值了。
你問我怎麼動態維護若干個值的最小值?搞個\(multiset\)不就行了嗎?
可是,如今問題又來了,若是每次修改一個點的權值(這個點固然是圓點啦),
那麼,一定會修改全部和它相鄰的方點,若是是一個菊花樹,而後咱們拼命修改根節點,這樣子複雜度就起飛了。
如今讓咱們打開腦洞,大力思考一下怎麼辦?
咱們強行讓方點的權值不包括它的父親(也就是隻算它的兒子)
若是求解的時候\(LCA\)是方點,則額外計算一下方點父親的權值
這樣子每一個圓點在修改的以後只須要向上修改給父親方點啦!
因而,咱們獲得了\(Tarjan\)+圓方樹+樹鏈剖分+線段樹+\(multiset\)的\(O(nlog^2n)\)的作法啦
(爲何要手寫可刪堆啊?\(multiset\)很差嗎?)
呼,終於寫完啦。
最後再來總的說一說仙人掌和圓方樹。
對於仙人掌,咱們對應的圓方樹惟一。
不管是廣義圓方樹仍是普通的圓方樹,和普通的樹相比,惟一的區別就是要額外考慮方點的貢獻。
總的來講,就是碼農+思惟題啦,只要想清楚方點的處理,一切都很好辦了。