CDQ (after Chen Danqi) divides the sequence of operations in half, recurses on both halves, and lets the left half’s updates act on the right half’s queries. Every update/query pair interacts at exactly one level. This removes one dimension at the cost of a factor, with only memory.

3D partial order

For points , count for each point how many points have , , (CSES-style “3D dominance”).

  1. Sort by (ties broken by , then ). Merge identical points first and give them multiplicities, otherwise equal points count each other inconsistently.
  2. CDQ on the index: after recursing, merge the halves by . Left elements insert into a BIT, right elements query.
  3. Undo the BIT insertions (don’t clear the whole BIT).
struct P { int a, b, c, cnt, id; };
vector<P> p; vector<ll> res; BIT bit;
void cdq(int l, int r) {                         // [l, r), p sorted by a
    if (r - l <= 1) return;
    int m = (l + r) / 2;
    cdq(l, m); cdq(m, r);                        // both halves are now sorted by b
    int i = l;
    for (int j = m; j < r; j++) {
        for (; i < m && p[i].b <= p[j].b; i++) bit.add(p[i].c, p[i].cnt);
        res[p[j].id] += bit.sum(p[j].c);
    }
    for (int k = l; k < i; k++) bit.add(p[k].c, -p[k].cnt);
    inplace_merge(p.begin() + l, p.begin() + m, p.begin() + r, [](auto& x, auto& y) { return x.b < y.b; });
}

time, memory. Point also dominates its own duplicates: add cnt - 1 to its answer after merging duplicates.

Removing time from dynamic problems

Treat the operation index as the first dimension. “Point updates and rectangle-sum queries, interleaved”: updates in the left half of time affect queries in the right half. Inside the merge, sweep by with a BIT over . That gives with memory, instead of a 2D segment tree or BIT of BITs ( memory).

The pattern works whenever:

  • contributions are additive (or otherwise mergeable) across updates;
  • an update affects a query iff it comes earlier in time and satisfies a static (time-independent) condition.

CDQ for DP

When depends on for with a condition needing a data structure (2D dominance, convex hull with non-monotone slopes), the values aren’t known in time for a simple sweep. Process in-order:

  1. cdq(l, m): left values become final.
  2. Apply the transitions from to (sort both halves, sweep with a structure).
  3. cdq(m, r).

Note the order differs from counting: the right half is recursed after the cross step. Examples: 3D LIS (longest chain in partial order), DP with convex hull trick where slopes and query points come in arbitrary order (build the hull of the left half, query it with the right half’s points sorted).

Higher dimensions

Nest: CDQ inside CDQ removes one more dimension each time, for dimensions. Beyond 4D, bitset approaches (: one bitset per dimension and value threshold, AND them together) or KD-trees are usually simpler.

Alternatives

ProblemCDQOther
2D dominance (static)unnecessarysort + BIT sweep
3D dominance, memoryBIT of sorted vectors ; KD-tree
point update + rectangle query offline2D BIT with compression; online: BIT of treaps or wavelet structures
dynamic CHTCDQ + static hull per levelLi Chao tree (usually easier)

Pitfalls

  • Ties: decide which equal coordinates count as dominated and enforce it in both the initial sort and the merge comparison.
  • Undo the structure exactly; clearing an BIT per call makes it .
  • Keep the merge stable, and make sure step order (recurse right before or after the cross step) matches counting or DP.

When not to use it

CDQ has a real constant factor and is fiddly to debug. Before writing it, check whether a plain offline sweep with one BIT suffices (two dimensions only), the queries can be sorted to remove a dimension outright, or a persistent segment tree answers it more directly — any of the three is less code than CDQ when it applies.