Pattern Reference
Offline CDQ Divide & Conquer
"CDQ divide-and-conquer for multi-dimensional offline queries. 3D partial order, dynamic convex hull, DP optimization."
Loading...
Deep Dive Tutorial
CDQ D&C solves problems of the form: "for each query point, count preceding points that dominate it in multiple dimensions." The time dimension is free (guaranteed: left half precedes right half). CDQ recursively processes: left half → compute left-on-right contributions → right half. The cross contribution step: sort both halves by x, use two pointers + Fenwick tree on y to count 2D domination. Result: O(n log²n).
CDQ D&C for 3D partial order
// 3D partial order: for each point (t,x,y), count points with t'<t, x'<=x, y'<=y
// CDQ: time dimension handled by divide (left half has smaller t)
// x dimension: sort + two-pointer
// y dimension: Fenwick tree
function cdq3D(points) { // points = [{t, x, y, idx}] sorted by t
const ans = new Array(points.length).fill(0);
const n = points.length;
const bit = new Array(n + 1).fill(0);
const upd = i => { for (; i <= n; i += i & (-i)) bit[i]++; };
const qry = i => { let s = 0; for (; i > 0; i -= i & (-i)) s += bit[i]; return s; };
const undo = [];
function solve(l, r) {
if (l === r) return;
const mid = (l + r) >> 1;
solve(l, mid); // process left half
// Compute contribution of left half on right half
const left = points.slice(l, mid + 1).sort((a, b) => a.x - b.x);
const right = points.slice(mid + 1, r + 1).sort((a, b) => a.x - b.x);
let li = 0;
for (const rp of right) {
while (li < left.length && left[li].x <= rp.x) {
upd(left[li].y); undo.push(left[li].y); li++;
}
ans[rp.idx] += qry(rp.y);
}
// Undo BIT updates
while (undo.length) { const y = undo.pop(); for (let i = y; i <= n; i += i & (-i)) bit[i]--; }
solve(mid + 1, r); // process right half
}
solve(0, n - 1);
return ans;
}Worked Problems
scissors
CDQ D&C template:
1. If base case, return
2. cdq(left half)
3. Compute left→right cross contributions (sorted by 2nd dim, BIT on 3rd dim)
4. Undo BIT changes
5. cdq(right half)
Key insight: The 1st dimension (time) is handled by divide — left half always precedes right. Reduce 3D to 2D using sort + BIT.
Applications:
- 3D partial order: O(n log²n)
- Dynamic inversion counting: O(n log²n)
- Offline range k-th smallest: O(n log²n)
- Any problem reducible to "count preceding points satisfying multi-dim constraints"
vs Persistent Segment Tree: Both solve offline range queries but with different constant factors. CDQ is cache-friendlier; PST uses O(n log n) extra space.
1. If base case, return
2. cdq(left half)
3. Compute left→right cross contributions (sorted by 2nd dim, BIT on 3rd dim)
4. Undo BIT changes
5. cdq(right half)
Key insight: The 1st dimension (time) is handled by divide — left half always precedes right. Reduce 3D to 2D using sort + BIT.
Applications:
- 3D partial order: O(n log²n)
- Dynamic inversion counting: O(n log²n)
- Offline range k-th smallest: O(n log²n)
- Any problem reducible to "count preceding points satisfying multi-dim constraints"
vs Persistent Segment Tree: Both solve offline range queries but with different constant factors. CDQ is cache-friendlier; PST uses O(n log n) extra space.