算是最後幾個完成這一章節學習的了,有不少思想和技巧都很好,須要好好學習算法
主要涉及兩個算法: \(Prim\) 算法和 \(Kruskal\) 算法學習
感謝lsp爲本文訂正作出的貢獻!spa
先跑一遍 \(Dij\) 求出 \(D_i\),再遍歷每一個點,統計有幾種修建方案
而後根據乘法原理,求出全部方案數便可
(感受和最小生成樹沒半毛錢關係是吧排序
Codeget
要求將整個圖的節點通電,因此發電站必定要有,並要結合電網來達到最小花費。
想象一下,電從哪來?不能憑空產出,只能從外邊接(感性理解/cy)
因此能夠建一個虛點(總電源),發電站只有鏈接總電源才能通電。class
因此這個題就轉化成,另設一個虛點將全部節點鏈接,而後建一個總的最小生成樹的板子題了原理
Code遍歷
由於給出的數據必定是最小生成樹,因此直接開始建樹
在建樹過程當中,每一次合併當成兩個聯通塊相連技巧
由於要求構成徹底圖,設兩個相連的聯通快分別爲 \(cnt_i\) 和 \(cnt_j\) 那麼兩個聯通塊之間最多能連 \(cnt_i \times cnt_j - 1\) 條邊(由於最小生成樹的邊已經統計過一次了因此要減一)
要求最小生成樹惟一,因此這些邊的邊權是對應的 \(e[i].w + 1\),統計後兩個聯通快記得和成一個聯通塊im
考慮建樹邊統計答案這種作法的正確性:其中的任何一條邊都不可能做爲最小生成樹的一條邊,由於有比他更優的邊已經鏈接了兩個聯通塊(類比 \(Kruskal\) 的貪心原理,可能這道題就是爲了理解這個算法的原理而存在的)
本質同嚴格次小生成樹,只是數據範圍略有不一樣
牛奶運輸不是嚴格次小,不過用嚴格次小的代碼居然也能過/jk
大致思路:建出最小生成樹 \(\to\) 用 \(LCA\) 預處理最大值的次大值 \(\to\) 遍歷全部不在最小生成樹上的邊,記錄統計後的答案哪一個更小 \(\to\) 切掉此題
一開始考慮先用白邊生成樹,再用黑邊生成樹。若是這樣作的話,生成的樹可能不是聯通的。換一種說法就是有可能有不是很優的白邊也能夠在生成樹上。
一個很奇妙的思路:
二分一個值,讓白邊減掉這個權值,而後跑最小生成樹,當所用數量恰好等於白邊而且權值最小時就是答案。注意最後的答案要加上減去的權值
正確性:經過改變白邊的權值來改變白邊在全部邊中的位置,減得越多白邊在排序中越靠前,在最小生成樹中的數量也越多,反之越少。所以具備單調性,能夠二分出最恰當的那個要減得權值
考慮一棵最小生成樹中,若是有其餘建樹方式,爲了保證權值和不變,那麼新加的邊與減掉的邊邊權必定是相同的
那麼咱們統計出全部邊權相同的邊,並將他們分組放在一塊兒。根據最小生成樹的貪心思想,先連小邊再連大邊。每連一種權值的邊,爆搜有多少種連邊方式。最後根據乘法原理統計答案便可
我在想一個正確性:有沒有一種狀況,能把兩條邊同時替換爲一大一小的兩條邊也能使其成爲一棵最小生成樹
證實:一開始就是從小到大連邊,因此小的邊的狀況已經統計上了,這種狀況在一次建樹後就不可能出現(題目中說無重邊和子環也是一個前提