Constructive problems ask you to build an object, or report that none exists. The difficulty is almost never the code. It’s finding the obstruction (an invariant) and a construction that works whenever the obstruction is absent.
Method
- Necessary conditions: parity, counts, sums, degree bounds, an invariant (Invariants).
- Brute force and print the solutions. Which inputs are solvable? What do the solutions look like?
- Guess a sufficient construction. Usually induction (extend or ), divide and conquer (solve halves, glue), or a closed-form pattern.
- Handle the small exceptions separately. Most constructions have two or three.
- Verify with a checker on all small inputs before submitting. Constructions are easy to get almost right.
- If direct construction doesn’t click, try visualising it by hand, modeling it as a graph, or working backwards/by complement/by symmetry — see Attacking a Problem for the general reformulation toolkit. If an attempt fails, pin down exactly why (counterexample, a violated invariant, an unjustified assumption) before trying the next idea.
Greedy constructions need a feasibility oracle: when you commit to the next piece, prove the rest is still completable. For the lexicographically smallest witness, try candidates in order and keep the first one whose residual instance passes the oracle. The oracle may be a counting inequality, a matching check (Hall), or a DP.
Recipes
| Recipe | Example |
|---|---|
| alternate extremes | maximises adjacent gaps and makes adjacent differences distinct |
| pair with | constant pair sums, symmetric arrangements |
| induction by 2 or 4 | extend a solution for to with a fixed gadget, plus base cases |
| recursive halves | Gray code, Hanoi, L-tromino tilings, sorting networks |
The binary-reflected Gray code is the cleanest instance: prefix to the -bit code and to its reversal. Adjacent codes within each half differ in one bit by induction, and the join (last code of the -half to first code of the reversed -half) differs in exactly the new top bit, since reversal makes that boundary pair identical on the low bits.
| powers of two | make subset sums or XORs independent; use bits to encode choices |
| linear patterns | , |
| colour or split by residue | odd and even positions, mod |
| most constrained first | biggest degree, largest value, fewest options |
| build the tree / skeleton first | then add extra edges or elements to adjust a count |
| fix the answer’s structure | “WLOG the answer has the form ”, then solve for parameters |
| interactive / adaptive | maintain a candidate set and halve it each step |
Permutations
| Goal | Construction |
|---|---|
| exactly distinct values of | (zigzag inward), then the remaining values in increasing order |
| derangement | cyclic shift ; for “far from identity” use cyclically |
| adjacent elements differ by | evens then odds (e.g. ); impossible for |
| pairwise distinct mod (0-indexed) | identity works iff odd; for even it’s impossible since |
| all prefix sums distinct mod | for even ; impossible for odd (proof below) |
| all prefix products distinct mod (permutation of ) | via inverses mod prime ; exists only for prime, , or |
| exactly inversions | place values from largest down; value placed with smaller ones after it adds inversions (treat position and value as separate coordinate systems — a swap can move the wrong statistic if you conflate them) |
| maximum | reverse () |
| given LIS length and LDS length | exists iff (proof below); concatenate decreasing blocks of size |
| exists iff (proof below); 4-cycles | |
| with (square root) | pair cycles of equal even length; odd cycles have their own roots |
Proof: two permutation existence criteria
Prefix sums distinct mod . This is exactly asking whether the cyclic group is sequenceable: a permutation of all group elements whose partial sums are themselves a permutation of the group. Gordon’s theorem (1961) settles this completely for cyclic groups: is sequenceable iff is even. The full proof is a careful group-theoretic argument beyond what’s useful to reproduce here; the explicit even- construction above realises the “if” direction, and the “only if” (odd impossible) is Gordon’s necessity result. Verified computationally below.
LIS , LDS achievable simultaneously iff : Necessity of : by the Erdős-Szekeres argument (proved in Invariants), assigning each element the pair (length of longest increasing run ending there, length of longest decreasing run ending there) gives distinct pairs, so if the longest increasing run is exactly and longest decreasing exactly , the pairs live in , needing — wait, this gives the upper bound directly; for the lower bound : the longest increasing run and longest decreasing run overlap in at most one element (an element can’t be in a strictly-increasing run of length and a strictly-decreasing run of length simultaneously at the same position unless trivially), so , i.e. . Sufficiency: partition into blocks of sizes as equal as possible (each , achievable exactly when and using blocks forces average block size iff ), arrange each block decreasing, and concatenate the blocks in increasing order of their value ranges — then the LDS is the largest block size (attained if some block has size exactly , arranged by the bound to ensure at least one block reaches size ) and the LIS is (one element from each block, since blocks are increasing in range).
exists iff : this requires to be an involution-like structure composed with the order-reversing permutation ; specifically . Since has order 2 (), must have order 4 (as , and rules out order 1 or 2), so decomposes into cycles of length 1, 2, or 4 — but length-1 fixed points would need giving , i.e. , only possible for one when is odd (and then that leftover fixed point breaks the order-4 structure needed elsewhere unless specifically balances it), and length-2 cycles would need on that cycle, contradiction — so cycles must have length exactly 4 (possibly with the single exception noted), requiring (minus at most one fixed point) divisible by 4, which works out to exactly.
Tested: all three existence criteria checked by brute force over all permutations of for small (up to 9-10), confirming the stated existence conditions match exhaustive search exactly.
Grids and matrices
| Goal | Construction |
|---|---|
| Latin square | |
| orthogonal Latin squares, odd | and |
| odd magic square | Siamese method: start top middle, move up-right, drop down on collision |
| no two adjacent cells equal | chessboard colouring, or |
| knight’s tour / Hamiltonian path on grid | Warnsdorff heuristic (always move to the cell with fewest onward moves), or snake patterns for king/rook moves |
| every row and column sum odd/even | fix the first , solve the last row and column; consistent iff total parities agree |
| covering a grid with paths or cycles | snake (boustrophedon) order |
Sequences and numbers
| Goal | Construction |
|---|---|
| all subset sums distinct | |
| distinct positives with sum | , then (needs it ) |
| no 3-term arithmetic progression | numbers whose base-3 digits avoid 2 (greedy Stanley sequence) |
| pairwise coprime | distinct primes; consecutive integers; Fermat numbers |
| gcd exactly for every pair | with distinct primes |
| overall but not pairwise coprime | e.g. : , , , yet — “collectively coprime” is strictly weaker than pairwise coprime |
| (XOR is addition without carries) | |
| numbers with XOR | any distinct numbers, then their XOR (fix collisions with a high bit) |
| duplicate an element to cancel it; the basis of many “pair up and cancel” constructions | |
| consecutive pair XORs to | build a target XOR value out of such pairs when only “rotate the low bit” moves are allowed |
| XOR of | for (proof below); blocks cancel |
| prescribed remainders | CRT — but check compatibility first when moduli aren’t pairwise coprime: and have a common solution iff , not automatically |
| represent as a sum of few special numbers | Lagrange (4 squares), Legendre (3 squares unless ), Goldbach-style searches for primes in practice, Zeckendorf for Fibonacci numbers |
Proof: XOR of has period 4
For any , : all four share the same bits above the lowest two (, , , for the same ), so those high bits appear 4 times each (cancelling), and the low two bits XOR to . So (prepending 0 changes nothing) telescopes to just the XOR of the trailing partial block . Write , : that block is .
- : block is , XOR .
- : block is , XOR .
- : block is ; the shared high bits appear 3 times (odd, survive), low bits XOR to , giving .
- : the full block , which cancels to 0.
Matching to exactly.
Graphs
| Goal | Construction |
|---|---|
| connected with exactly edges | any spanning tree, then unused edges () |
| realise a degree sequence | Erdős-Gallai condition, then Havel-Hakimi (repeatedly connect the largest-degree vertex to the next largest) |
Even degree sum is necessary, not sufficient
has an even sum () and every term , yet no simple graph on 4 vertices realises it: the two degree-3 vertices would each need to connect to all 3 others, forcing both degree-1 vertices up to degree 2. The full Erdős-Gallai condition (a prefix inequality for every ) is what’s actually necessary and sufficient — checking only the sum and the max term is not enough.
| bipartite degree sequences | Gale-Ryser, then greedy |
| tree with given degrees | any degree sequence of positive integers summing to ; build with a Prüfer-like greedy (leaf to smallest non-leaf) |
| diameter exactly | a path of length with the rest attached near the middle |
| -regular graph | circulant: (plus for odd , even ) |
| triangle-free with most edges | ; generally the Turán graph |
| exactly shortest paths, or spanning trees, etc. | binary encoding: a chain of “diamond” gadgets, one per bit of |
| graph with chromatic number and no triangle | Mycielski construction |
| tournament where each vertex beats exactly | for odd |
| orient edges so in/out degrees differ by | Euler tour after pairing odd-degree vertices with a dummy |
Binary-encoding gadgets are the standard trick for “construct a graph or grid with exactly of something” with small size: build a structure that doubles a count per layer, plus optional “+1” connections for the set bits of .
Always verify
Write a checker and run it on every up to 8-10 and on random inputs. Check boundary sizes and the impossible cases explicitly.
Construction methods in depth
A constructive problem asks for any object with given properties (or “-1”). The work is finding a family of objects you can describe in one line and prove correct. The overview with short recipes is Constructive Algorithms; this chapter goes deeper by object type.
Methods
| Method | How it goes | Typical use |
|---|---|---|
| brute force, then pattern | enumerate all objects for , print the valid ones, look for a regular family | permutations, small grids |
| necessary conditions first | prove what any answer must satisfy (counts, parity, sums, bounds); then build an object meeting exactly those conditions | ”-1 or construct” problems |
| induction or | extend an answer for a smaller by a gadget at the end; handle the base cases by hand | permutations, graphs, tilings |
| extremes first | place the largest / most constrained element first, where it hurts least | permutations with conditions on neighbours |
| split into independent parts | parity classes, residues, bits, connected components | swaps at distance , bitwise conditions |
| greedy with a feasibility test | build left to right; at each step take the smallest choice that keeps the rest feasible | lexicographically smallest answers |
| randomization | random object plus verification; repeat | colourings, hash-like objects, output-only tasks |
Always ship a checker
Write the checker before the construction: it verifies the object against the statement in the most naive way. Run the construction for every small and every parameter, and compare “construction says impossible” with “brute force finds nothing”. All the code in this chapter was tested this way.
Example: all adjacent differences distinct
Find a permutation of whose adjacent differences are pairwise distinct. Alternate between the smallest and the largest unused value:
vector<int> distinctDiffPerm(int n) { // |p[i+1] - p[i]| are n-1, n-2, ..., 1
vector<int> p; int lo = 1, hi = n;
for (int i = 0; i < n; i++) p.push_back(i % 2 == 0 ? lo++ : hi--);
return p;
}Proof: the zigzag has differences n−1, n−2, ..., 1
The sequence is . Each step goes from the current low pointer to the current high pointer or back, and the gap between the two pointers shrinks by exactly one after each step, starting at . So the differences are , all distinct. Checked for all .
Related: Observation Toolkit.