1. 程式人生 > >naive的圖(線段樹合併)

naive的圖(線段樹合併)

Preface

Debug了半個晚上,有必要總結一下。

今天打了兩三個線段樹合併的題,深深感到資料結構對手殘黨的惡意。動輒上百行的程式碼,一定要非常熟練才有可能在真正比賽的時候打出來。

題意

有一張無向圖,nn個點,mm條邊,常數LL。有點權(記為c[i]c[i])、邊權(d[u,v]d[u, v])。求滿足u<v,c[u]c[v]Lu<v,|c[u]-c[v]|\geq L的點對之間的路徑上最大邊權的最小值。

講的還沒有原題清楚。。。

有一張 n 個點,m 條邊的帶權無向圖。第 i 個點的顏色為 ci。d(s, t)表示從點 s 到點 t 的權值最小的路徑的權值,一條路徑的權值定義為路徑上權值最大的邊的權值。求所有滿足 u < v, |cu − cv| ≥ L 的點對 (u, v) 的 d(u, v) 之和。

思路

線段樹合併

首先很明顯的,對於邊權最大的最小值,可以用KruskalKruskal的貪心思路解決。

但是與模板不同的是,這題在合併fa[u]=vfa[u]=v時,還需統計答案。那麼又很容易想到用一顆權值線段樹維護每個已合併的塊的所有點的顏色,在合併兩個塊時列舉其中一個塊的所有點,在另一個塊中查詢顏色與它符合要求的點,更新答案。

但是又有兩個問題:

  1. 對每一個節點都開一顆線段樹,明顯空間不夠。
  2. 合併的複雜度好像有點不對勁。

對於Question1,可以用動態開點線段樹,甚至不用離散化,總空間複雜度大概在O(nlog(1e9))O(nlog(1e9))

log(1e9))左右。但是我寫的線段樹合併出了一點問題,加了離散,再回收節點,才脫離RE。實際上是沒有必要的?看高分程式裡面有好多沒有回收節點的呢

對於Question2,有一種玄學演算法叫啟發式合併。因為人類的智慧,這樣合併只需要O(nlogn(time&ThickSpace;per&ThickSpace;merge))O(nlogn*(time\;per\;merge)),那麼在此題的線段樹合併上(這題每個原始線段樹只有一條鏈)複雜度就是O(nlog2n)O(nlog^2n)

再記錄幾個傻逼問題,如果有人也看了,把這些當做前車之鑑,不要再翻車了:

  1. 特判L=0時忘記寫啟發式合併的交換,T了好幾發。
  2. 沒有預處理c[i]+L-1和c[i]-L+1的離散值,複雜度可能整整多了一個log,然而我一點意識都沒有。
  3. 空間問題,預估空間不夠的情況下可以寫個TrashBin回收一下。

程式碼就不放了,調到最後瀕臨崩潰,醜的不能看。

Kruskal重構樹+二維數點

題解思路,其實就是把線段樹合併變成數點。

先建出 Kruskal 重構樹,每條邊的貢獻次數為它連線的兩個子樹之間的顏色之差大於等於 L 的點對數,可以發現 ∑ min(size(lef tchildi), size(rightchildi)) = O(n log n)。 對於每條邊我們列舉 size 較小的那棵子樹內的點,算出在另一棵子樹中能與它組成點對 的點的個數。 這個問題實際上就是詢問在 dfs 序的一段區間上並且顏色不在一段區間內的點數,二維數點問題可以離線樹狀陣列完成。 總的時間複雜度為 O(m log m + n log2n)。

其實我不會