本頁面將完整介紹 CDQ 分治。
CDQ 分治是一種思想而不是具體的演演算法,與動態規劃類似。目前這個思想的拓展十分廣泛,依原理與寫法的不同,大致分為三類:
CDQ 分治的思想最早由 IOI2008 金牌得主陳丹琦在高中時整理並總結,它也因此得名。
這類問題多數類似於「給定一個長度為 nn 的序列,統計有一些特性的點對 (i,j)(i,j) 的數量/找到一對點 (i,j)(i,j) 使得一些函數的值最大」。
CDQ 分治解決這類問題的演演算法流程如下:
可以看到 CDQ 分治的思想就是不斷地把點對通過遞迴的方式分給左右兩個區間。
在實際應用時,我們通常使用一個函數 solve(l,r) 處理 l≤i≤r,l≤j≤r 的點對。上述演演算法流 程中的遞迴部分便是通過 solve(l,mid) 與 solve(mid,r) 來實現的。剩下的第二類點對則需要額外設計演演算法解決。
分析:
三維偏序(陌上開花)是 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分治和整體二分都是基於分治的思想,把複雜的問題拆分成許多可以簡單求的解子問題。但是這兩種演演算法必須離線處理,不能解決一些強制線上的題目。不過如果題目允許離線的話,這兩種演演算法要比線上解法(如樹套樹)快很多。