Split into independent halves, recurse, and handle what crosses the split. The combine step is where the work and the cleverness live. Its cost sets the total: combine gives , combine gives .
Template
ll solve(int l, int r) { // [l, r)
if (r - l == 1) return base(l);
int m = (l + r) / 2;
ll res = solve(l, m) + solve(m, r);
res += crossing(l, m, r); // objects with one part in each half
return res;
}Recursion depth is , so it’s safe. Allocate merge buffers once, globally.
Counting pairs across the middle
Every pair is “crossing” at exactly one level of the recursion, the one where and separate. With both halves sorted (merge sort order), crossing pairs are usually counted with two pointers.
Proof: every pair is crossing at exactly one level
Fix . At the root, either they land in the same half (and recursion continues on that half) or in different halves (crossing, counted here, and the recursion never revisits this pair again since the two indices are never reunited in a single call). If they land in the same half, apply the same argument one level down. Since each level strictly shrinks the interval containing both until it can no longer hold both halves, this process terminates in at most steps at the unique level where first separate. So the pair is crossing at exactly one level, not zero (eventually some level must separate and , since a single-element interval cannot) and not more than one (once separated, recursion never reconsiders the pair).
| Problem | Crossing step |
|---|---|
| inversions; pairs with | two pointers during the merge |
| pairs with | sorted halves, two pointers |
| subarrays with sum in | sorted prefix sums of each half |
| closest pair of points | strip of width sorted by , at most 7 neighbours each |
| 3D dominance, offline 2D updates | CDQ |
Subarrays crossing the middle
For “count / optimise over all subarrays of ”, the subarrays with split into a suffix of the left half and a prefix of the right half. Compute an aggregate for each left start (scanning left from ) and each right end (scanning right from ), then combine them with sorting, two pointers or a data structure. Example: count subarrays where length (permutation subarrays): split by whether the max and the min fall on the left or the right; each case is linear with two pointers. total.
Divide at the maximum (unbalanced D&C): for “subarrays whose maximum is ”, the split point is the maximum, not the middle. Iterate over the shorter side and query the longer side with a precomputed structure. Since each element is on the shorter side times (like small-to-large), the total is times the query cost. This is a traversal of the Cartesian tree.
Proof: each element is on the shorter side times
Charge an element once every time the subarray containing it is split and ends up on the side chosen for iteration (the shorter one). The subarray containing has some length before the split; since is on the shorter side, the new subarray containing has length . So each charge at least halves the length of the interval containing , starting from and bottoming out at , giving at most charges. Summing over all elements, the total number of (element, split) charges — which is exactly the total work of “iterate over the shorter side” across the whole recursion — is .
Offline range queries by D&C
Answer queries for an operation that’s hard to combine (knapsack over a range, a “best subsequence” DP, path counts in a grid strip): recurse on with mid . Queries with are answered at this node; the others go to the side that contains them. At the node, compute for every the DP of (a suffix DP growing leftwards), and for every the DP of , then combine the two DP states for each query.
- Range 0/1 knapsack queries “best value of with capacity ”: .
- Grid reachability queries “can reach moving right/down”: split rows at the middle, bitset of reachable middle-row cells from each source and to each target, .
- It’s the disjoint-sparse-table idea applied to arbitrary DP states (Disjoint Sparse Table).
Divide and conquer on other axes
- On time (segment tree over the query timeline): each element’s lifetime is tree nodes; DFS the tree with a rollback structure. That’s offline dynamic connectivity, bipartiteness and XOR basis under deletions (Dynamic Connectivity).
Proof: a lifetime decomposes into segment-tree nodes
This is the same fact that makes range updates on a segment tree cost nodes: build a segment tree over the time slots (leaves = individual moments). A lifetime is covered by the canonical decomposition into disjoint segment-tree nodes used by any range-update-point-query segment tree. At each of the depths, the decomposition contains at most 2 nodes (one partially covering the left boundary, one the right; internal depths in between contribute at most 2 fully-covered maximal nodes by the standard segment-tree range-decomposition argument). So the lifetime is stored in nodes total, and inserting it into all of them costs per element.
- On values (“overall binary search”): recurse on the answer range with the queries whose answer lies there, applying the updates with values in to a BIT to split the queries. It’s parallel binary search written recursively, and it handles range -th smallest with updates in .
- On a tree: centroid decomposition gives depth , and every path passes through exactly one centroid in its component (Centroid Decomposition).
- On DP layers: monotone optimum splits, per layer (D&C Optimization).
- On the plane: closest pair, Delaunay triangulation, convex hull by merging.
Classic D&C algorithms
| Algorithm | Recurrence | Time |
|---|---|---|
| merge sort, inversion count | ||
| closest pair of points | (presorted by via merging) | |
| Karatsuba multiplication | ||
| Strassen | ||
| FFT | ||
| median of medians | ||
| maximum subarray (as a D&C) | with a 4-value summary | |
| skyline / union of rectangles in 1D | merge two skylines | |
| polynomial multipoint evaluation | product tree, remainder tree |
This is the master theorem (restricted to polynomial , which covers every row above; Complexity and Analysis states the fully general version and the non-polynomial Akra–Bazzi extension).
Proof: the recursion-tree bound for
At recursion depth there are subproblems, each of size , so the non-recursive work at that depth totals where . Summing over the depths of the tree:
- If : the geometric series converges to a constant, so the total is dominated by the root term, .
- If : every depth contributes exactly , and there are depths, giving . This is the merge sort / FFT case (, : ).
- If : the series is dominated by the deepest term, depth , giving (since ). This is the Karatsuba case (: ) and the Strassen case (: ).
Proof: median of medians is
This recurrence has an unequal split, (finding the pivot recurses on medians-of-5; partitioning around it recurses on at most elements, since the pivot is guaranteed greater than at least elements in each of at least half the groups of 5, and symmetrically less than at least in the other half, eliminating elements). The two branch sizes sum to , i.e. a fixed fraction of . By induction, assume for all (some constant to be fixed): then . Choosing with , i.e. , closes the induction, giving .