1. 程式人生 > 其它 >cdq 分治、整體二分、二進位制分組以及高維數點問題總結

cdq 分治、整體二分、二進位制分組以及高維數點問題總結

隨便寫寫吧。。。。。。。。。

小螺號呀滴滴地吹,ycx 呀 xjb 寫。

資料結構非經典演算法

cdq 分治

傳統分治演算法是當前區間分成兩個區間遞迴下去各解決各自的。cdq 分治是不僅把兩部分的子問題都解決了,還讓左部分對右部分進行貢獻(右對左也行?)。如果左對右貢獻的時間複雜度僅與當前處理區間長度 \(n\) 有關,並且是其 polylog,設為 \(f(n)\),則總複雜度為 \(\mathrm O(f(n)\log n)\)​(這玩意是上界是顯然的,同階的話就感性理解一下吧,大概要用 master 定理?)。

較常見的應用:

  1. 數線性結構上(樹上可以用 cdq 的拓展——點分治,但我還沒學/ll)符合某種條件的數對,左對右貢獻就是一個在左部分一個在右部分的數量,由於有 \(mid\)
    這個大壩在中間隔著,一般比原問題簡單得多,代價是一個 log。
  2. 將離線動態資料結構問題轉化為離線靜態問題,代價是一個 log。前提是修改對詢問的貢獻有交換律、結合律,而且互相獨立。方法是每次左部分的所有修改對右部分的所有詢問做貢獻,是靜態的。
  3. 優化 dp。dp 和二分這兩玩意看上去是線上,但又不完全是,所以取名曰「半線上」。所以解決 dp 的這 cdq 事實上是改進版本的 cdq,必須是 solve 了左部分,然後左對右貢獻,然後 solve 右部分這樣一個順序。因為必須等左部分 dp 值算好之後才能貢獻。

二進位制分組

只做過一道題,不常用就是了

cdq 想把動態問題轉靜態問題,需要允許離線。如果強制線上,我們依然有辦法將線上動態問題轉化為線上靜態問題,以一個 log 的代價,前提也是修改對詢問的貢獻有交換律、結合律、互相獨立。

方法是對任意時刻設目前進行了 \(x\) 次修改,將 \(x\) 二進位制拆分,實時對每個位權為 \(i\) 的二進位制位維護連續 \(i\) 個修改的預處理成果,位權越小維護的修改越新。新加一個修改的時候,令 \(x\) 加一,取低位極長連續 \(1\) 段,加一就是讓這些 \(1\) 全變成 \(0\),然後上一位變成 \(1\),實現就是把這些 \(1\) 位合併起來加上新加修改放到新的 \(1\)​ 位,可以直接暴力重構。這樣子等效於線上靜態。查詢就直接到 \(\log\) 個修改塊裡面依次查詢,把貢獻弄起來。

設預處理 \(n\)​ 個修改複雜度為 \(n\) 的 polylog \(f(n)\)

,複雜度證明:

\[\begin{aligned}\mathrm T(n)&=\sum\limits_{i=1}^nf(\mathrm{lowbit}(i))\\&=\sum\limits_{i=1}^{\lfloor\log_2n\rfloor}f\!\left(2^i\right)\left\lfloor\dfrac{n}{2^i}\right\rfloor\\&=\mathrm O(f(n)\log n)\end{aligned} \]

整體二分

我們知道二分這玩意是「半線上」,如果你就把它當線上做,一次回答一個 chk 的話,那免不了受線上問題的侷限,比如要預處理一大堆資訊,維護的資料結構套層也比較厚。我們有整體二分演算法能加大二分的 chk 們的離執行緒度,前提是題目允許離線。

考慮把所有待二分詢問離線下來存到一個序列裡,然後一起二分,一開始將它們的二分範圍設成一樣然後一起 chk。chk 出來一些分到左邊一些分到右邊,繼續遞迴下去一起 chk。注意到這裡某個二分的 log 步之間還是線上關係,但是若干個二分是平行的,整體二分就利用了這一點充分發揮離線的優勢一起 chk。

設處理 \(n\) 個詢問的 chk 的複雜度是僅關於 \(n\) 的 polylog \(f(n)\),則總複雜度為 \(\mathrm O(f(n)\log v)\),其中 \(v\) 是二分範圍,不劣於傳統二分。整體二分過程中可能不止維護詢問序列,還維護了一些對 chk 有貢獻的修改序列,這時候需要保證它們每次分裂的時候最多被分到一邊(詢問的話是自然),此時 \(n\) 是所有序列的總長度。另外若 \(v\)​​ 過大,當詢問序列為空時要及時停下來,不然複雜度爆炸。

遇到操作 \(x\)\(mid\geq x\) 都有貢獻,這時候有可能不能只分到一邊這種情況,通常有兩種辦法:將待 chk 詢問及時減掉 \([l,mid]\)​ 的貢獻,這樣就可以放心的把前面的貢獻只分到前面啦;若無法實現「減掉」,可以令 \([l,mid]\) 子問題做完後,\(\leq mid\) 的操作已經被貢獻完畢了,實現是該撤銷還是撤銷,但是到不繼續往下遞迴的時候要把當前操作序列全部貢獻上。

高維數點問題

指的是在 \(d\) 維空間中,有若干個點,每個點有個貢獻(交換律、結合律、獨立),求第 \(i\) 維不超過 \(x_i\) 所有點貢獻和。值域 \(v\),動態指的是動態加點。

如果是第 \(i\)\(\in[l_i,r_i]\) 這種限制可以差分轉化,當然是在貢獻可減的情況下。

離線情況的 cdq

離線靜態 \(d\)​ 維數點,可以通過給修改、詢問的混合序列按第一維排序(注意排序一定要是穩定的,不然修改可能跑到詢問後面去!),就變成了一個 \(d-1\) 維動態數點。而 \(d\) 維動態數點可以以一個 log 的代價變成 \(d\) 維靜態數點。按照這個思路遞迴下去。

邊界是靜態一維數點,不考慮排序 / 二分複雜度是線性。那麼靜態 \(d\) 維數點是個 \(d-1\) 個 log,動態就是 \(d\) 個。考慮上排序的話,可以發現維數稍高都被平行化掉了,唯有靜態一維增加到了 1log。

其實動態一維寫 cdq 就是所謂的「歸併排序寫逆序對」,為了平行化掉排序還非得歸併,直接 stable_sort 是 2log(當然更高維就直接 stable_sort 啦),比較麻煩,還是直接 BIT 好寫。

線上情況的線段樹套層

線上二維靜態可以主席樹 1log,更高維的 \(d\) 維靜態的套層線段樹我也不知道能不能可持久化啊啊啊,反正按動態做 \(d\) 個 log 是沒問題。

線上動態的話,\(d\) 維就套 \(d\) 個線段樹,最外層可以換成 BIT(除非貢獻性質不太好,不可減),裡面都是動態開點線段樹,不然空間不夠。空間 \(d-1\) 個 log,時間 \(d\) 個 log。線上三維動態可以二進位制分組套主席樹複雜度不變,再高維的話二進位制分組轉化為靜態關鍵是線上高維靜態咱不會做啊 5555

應該沒有出題人毒瘤到出在線的高維數點吧(高維指的是 3 維以上)。。。。

bitset 奇技淫巧

如果真的是「數」點,也就是每個點權為 \(1\)​,合併貢獻是加法,那有個奇技淫巧。考慮第 \(i\) 維不超過 \(x_i\) 的點的集合,用 bitset 存,然後把 \(d\) 維交起來即可。

實現不需要什麼「可持久化 bitset」,直接對每維掃一遍字首和預處理即可,前提是能存的下。時空複雜度都是 \(\mathrm O\!\left(\dfrac{dn^2}w\right)\)。注意到 \(d\) 這裡作為一個因子成在前面,而不是指數,所以維數上升沒什麼影響。

珍愛生命,遠離抄襲!