專題:CDQ 分治
本頁面將完整介紹 CDQ 分治。
簡介
CDQ 分治是一種思想而不是具體的演算法,與動態規劃類似。目前這個思想的擴充十分廣泛,依原理與寫法的不同,大致分為三類:
- 解決和點對有關的問題。
- 1D 動態規劃的最佳化與轉移。
- 透過 CDQ 分治,將一些動態問題轉化為靜態問題。
CDQ 分治的思想最早由 IOI2008 金牌得主陳丹琦在高中時整理並總結,它也因此得名。
解決和點對有關的問題
這類問題多數類似於「給定一個長度為 nn 的序列,統計有一些特性的點對 (i,j)(i,j) 的數量/找到一對點 (i,j)(i,j) 使得一些函式的值最大」。
CDQ 分治解決這類問題的演算法流程如下:
- 找到這個序列的中點 ;
- 將所有點對 (i,j)(i,j) 劃分為 33 類:
- 1≤i≤mid,1≤j≤mid 的點對;
- 1≤i≤mid,mid+1≤j≤n 的點對;
- mid+1≤i≤n,mid+1≤j≤n 的點對。
- 將 (1,n)(1,n) 這個序列拆成兩個序列 (1,mid)(1,mid) 和 (mid+1,n)(mid+1,n) 。此時第一類點對和第三類點對都在這兩個序列之中;
- 遞迴地處理這兩類點對;
- 設法處理第二類點對。
可以看到 CDQ 分治的思想就是不斷地把點對透過遞迴的方式分給左右兩個區間。
在實際應用時,我們通常使用一個函式 solve(l,r) 處理 l≤i≤r,l≤j≤r 的點對。上述演算法流 程中的遞迴部分便是透過 solve(l,mid) 與 solve(mid,r) 來實現的。剩下的第二類點對則需要額外設計演算法解決。
典型例題1:LOJ112/洛谷P3810 三維偏序(陌上開花)
分析:
三維偏序(陌上開花)是 CDQ 分治的經典問題。
假設我們現在寫好了 solve (l, r) ,並且透過遞迴搞定了 solve (l, mid) 和 solve(mid+1,r) 。現在我們要做的,就是統計滿足 l≤i≤mid,mid+1≤j≤r 的點對 (i, j)(i,j) 中,有多個點對還滿足 i<j,ai<aj,bi<bj 的限制條件。
稍微思考一下就會發現,那個 i<j 的限制條件沒啥用了:既然 i 比 mid 小, j 比 mid 大,那 i 肯定比 j 要小。 現在還剩下兩個限制條件: ai<aj 與 bi<bj , 根據這個限制條件我們就可以列舉 j , 求出有多少個滿足條件的 i。
為了方便列舉,我們把 (l,mid) 和 (mid+1,r) 中的點全部按照 a 的值從小到大排個序。之後我們依次列舉每一 個 j , 把所有 ai<aj 的點 i 全部揷入到某種資料結構裡(這裡我們選擇樹狀陣列)。此時只要查詢樹狀陣列裡有多少個點的 b 值是小於 bj 的,我們就求出了對於這個點 j ,有多少個 ii 可以合法匹配它了。
當我們揷入一個 b 值等於 xx 的點時,我們就令樹狀陣列的 xx 這個位置單點 +1+1,而查詢樹狀陣列裡有多少個點小於 xx 的操作實際上就是在求字首和,只要我們事先對於所有的 bb 值做了離散化,我們的複雜度就是對的。
對於每一個 j,我們都需要將所有 ai<aj 的點 i 揷入樹狀陣列中。由於所有的 i 和 j 都已事先按照 aa 值排好序, 這樣的話只要以雙指標的方式在樹狀陣列裡揷入點,則對樹狀陣列的揷入操作就能從 O(n2) 次降到 O(n) 次。
透過這樣一個演算法流程,我們就用 O(nlogn) 的時間處理完了關於第二類點對的資訊了。此時演算法的時間複雜度 是 T(n)= T( n/2 ) + T( n/2 ) + O( nlogn )= O(nlog2n)。【三維偏序(陌上開花)-參考程式碼-CDQ分治】
CDQ分治的限制
- 題目允許離線操作
- 修改操作對詢問的貢獻獨立,且修改之間互不影響
- 修改對答案的貢獻是確定的,與判定標準無關
CDQ分治和整體二分
CDQ分治和整體二分都是基於分治的思想,把複雜的問題拆分成許多可以簡單求的解子問題。但是這兩種演算法必須離線處理,不能解決一些強制線上的題目。不過如果題目允許離線的話,這兩種演算法要比線上解法(如樹套樹)快很多。