啓發式合併(堆、set、splay、treap)/線段樹合併學習小記

啓發式合併

  • 剛聽到這個東西的時候,我是相當蒙圈的。特別是“啓發式”這三個字莫名的裝逼,因此之前一直沒有學。
  • 實際上,這個東西就是一個SB貪心。
  • 以堆爲例,若我們要合併兩個堆a、b,我們有一種極其簡單的做法:那就是比較一下它們的大小,將小的堆的每個元素依次插入到大的堆中。不妨設|a||b| ,則時間複雜度即爲:O(|a|log2(|a|+|b|))
  • 這個東西看似很慢,但當點數較小的時候,我們可以證明複雜度是可被接受的。

  • 比如我們要合併n個堆,這n個堆共有m個點。設這n個堆={s1,s2,s3,...,sn}
  • 首先,我們合併s1s2 ,變成一個新的堆t1
  • 然後,我們合併t1s3 ,變成一個新的堆t2
  • ……
  • 以此類推,我們最終可以合併出一個堆tn1

  • 合併堆a、b時,記1次操作爲將a中的一個元素插入b(或將b中的一個元素插入a)。
  • 可以發現,第1次合併操作數|s2| ,第2次合併操作數|s3| ……第i次合併操作數|si+1|
  • 因此,總操作數i=2n|si|m 。而每次操作又是O(log2m) 的複雜度。因此:
  • 時間複雜度:O(n+mlog2m)

推廣

  • 啓發式合併也可以用到set、splay、treap等平衡樹上去。
  • 若我們要合併兩棵平衡樹a、b,也是先比較大小,將小的平衡樹的每個元素依次插入大的平衡樹。囿於插入的時間也是O(log2n) ,因此總複雜度還是O(|a|log2(|a|+|b|))
  • 注意:這裏的合併並非treap的merge。merge(a,b)是強行讓a所有元素的鍵值(要滿足二叉排序樹的性質的那個值)均小於b所有元素的鍵值,所以可以O(log2n) 做到;而這裏要合併的兩棵平衡樹a、b的鍵值可能是交錯不齊的。

線段樹合併

  • OI中常常遇到一些題目,要將若干物件不斷合併,維護信息。
  • 如果合併的順序不對,堆/平衡樹的啓發式合併會很慢。比如當你分治+啓發式合併的時候,時間複雜度就變成O(n(log2n)2) 了。
  • 這個時候,就需要線段樹合併。

  • 對於這個,相信大家都想得出下面這種合併步驟:
    這裏寫圖片描述
  • 爲了方便確定一棵樹是否爲空,我們動態開點。
  • 比如,我們合併兩棵權值線段樹:
    這裏寫圖片描述
  • 顯然,這麼做的複雜度與兩棵樹公共的節點數成正比。
  • 但是,假設我們要合併多棵線段樹呢?

  • 假設我們要合併n棵線段樹,定義勢能函數Φ(n) 爲它們的節點個數和。
  • 每次合併線段樹a、b時,設其公共點數爲c,則合併後的Φ(n) 減少c,而時間複雜度增加c。
  • 因此,時間複雜度應≤節點個數和。
  • 當線段樹中總共有m個元素時(比如n棵權值線段樹,只存有m個數),每個元素都可以動態開闢至多log2n 個節點。因此,此時的時間複雜度應爲O(n+mlog2n)
  • 注意:此時的時間複雜度並不受合併順序的限制。換句話說,不論你按什麼順序合併,只要你是合併n棵只有m個元素的線段樹,時間複雜度就是O(n+mlog2n)

例題

【BZOJ 2212】【Poi2011】 Tree Rotations
【JZOJ5800】【洛谷P4416】 [COCI2017-2018#1] 被單

發佈了114 篇原創文章 · 獲贊 52 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章