Proof is what turns “passes the samples” into “is correct”. You don’t need full rigour in a contest, but you need the key lemma. The patterns below cover most of it.
General tools
| Tool | Shape | Typical use |
|---|---|---|
| direct / loop invariant | state a property; show it holds initially, is preserved, and implies the answer at termination | binary search, two pointers, sweeps |
| pigeonhole (simple) | items into boxes forces one box with | hashing collisions, cycle existence, 100 prisoners cycle bound |
| pigeonhole (generalized) | items into boxes forces one box with | at least one of cases must handle load |
| pigeonhole (averaging) | if the average of values is , at least one value is (and at least one is ) | existence of a “good enough” element without constructing it |
| induction | base case plus “smaller cases imply this one”, on size, depth, value, or number of operations | DP, tree algorithms, recursive constructions |
| strong induction / minimal counterexample | assume all smaller cases, or take the smallest failure and build a smaller one | graph theory, number theory |
| contradiction | assume the property fails and derive an impossibility from optimality or an invariant | greedy optimality |
| construction | an explicit procedure plus proof that each step is legal, it terminates, and the output is valid | existence claims |
| bound + construction | prove , then build a solution of value | “minimum number of operations” |
| double counting | count one set two ways | identities, degree arguments |
| bijection | build a one-to-one map between two sets | Catalan families, partition identities |
| probabilistic method | if a random object has the property with positive probability, one exists | existence of good colourings, hashing bounds |
A loop invariant must be true mid-loop, not only at the end. For lower_bound on : everything below is and everything at or after is . Each step preserves this, and gives the answer.
Greedy
Exchange argument. Take any optimal solution . At the first place it differs from greedy, swap in the greedy choice and show the result is still feasible and no worse. By induction some optimal solution agrees with greedy everywhere.
Adjacent exchange for ordering problems. For a cost of a sequence, compare with . If the difference depends only on and , greedy sorts by the key that makes ” first” better. You must check the induced relation is a strict weak order (transitive). Example: minimise by sorting on (Smith’s rule).
Greedy stays ahead. Show that after steps greedy’s progress measure is at least as good as any solution’s after steps. Interval scheduling: the -th greedy interval ends no later than the -th interval of any feasible schedule.
Matroids. If the feasible sets form a matroid (hereditary plus the exchange property), the weight-sorted greedy is optimal for every weight function. That covers MST (graphic matroid), linearly independent sets (linear matroid), and scheduling unit jobs with deadlines (transversal/scheduling matroid). Bipartite matching is not a matroid; it’s an intersection of two partition matroids (Matroids).
Cut and cycle properties. For MST: the lightest edge crossing a cut is in some MST, and the strictly heaviest edge of a cycle is in none. Kruskal, Prim and Borůvka are all instances.
Counterexamples are part of the craft: before trusting a greedy, try to break it with 3-4 elements by brute force.
DP
- Say exactly what means, in words.
- Optimal substructure: if the part of an optimal solution inside subproblem weren’t optimal, replacing it would improve the whole. This replacement argument also tells you what the state must remember: everything the replacement could affect.
- Completeness: the transition enumerates every possible last (or first) decision, so the optimum is among the candidates. Soundness: every candidate is a feasible solution.
- Induct along a topological order of states. Check base cases and that unreachable states can’t leak in.
Where the replacement argument fails, the state is missing information. Longest simple path on a general graph depends on the set of visited vertices; on a DAG it doesn’t, so the vertex-only DP works.
DP optimisation proofs: monotonicity of the optimal split point () follows from the quadrangle inequality for . Convexity of in the number of groups justifies the Aliens trick. Both are covered in the DP chapter.
Graph algorithms
| Algorithm | Key lemma |
|---|---|
| BFS | vertices leave the queue in non-decreasing distance; induct on distance |
| Dijkstra | when is popped, is final: any shorter path leaves the settled set through an edge to some with , and non-negative weights make the rest of the path non-negative |
| Bellman-Ford | after rounds, shortest walk with edges; a relaxation in round proves a negative cycle |
| Floyd-Warshall | after phase , is shortest using intermediates in |
| DFS, white-path theorem | becomes a descendant of iff at time is discovered there’s a path of unvisited vertices from to ; this gives edge classification and SCC proofs |
| Tarjan SCC / bridges | low-link: is the smallest discovery time reachable via tree edges then one back edge; is an SCC root iff |
| Topological sort | a DAG has a source; removing it leaves a DAG; equivalently, reverse DFS finishing order |
| Max-flow = min-cut | if the residual graph has no - path, the vertices reachable from define a cut whose capacity equals the flow |
| Kuhn / Berge | a matching is maximum iff there’s no augmenting path |
| König | in bipartite graphs, max matching = min vertex cover (from the residual reachability of the max flow) |
| Hall | a perfect matching of the left side exists iff for every |
| 2-SAT | satisfiable iff no and share an SCC; assign true iff its SCC comes later in topological order |
Data structures
- Invariant first: heap order, BST order, “each segment tree node stores the aggregate of its range with all its ancestors’ tags applied”, “the deque is increasing”.
- Complexity: potential functions. Splay: gives the access lemma. Segment tree beats: = number of distinct values in nodes, or the number of “tags”. Union by size: an element’s set doubles each time its root changes.
- Randomised structures: treap depth is in expectation, by the same analysis as random BST insertion and quicksort: is an ancestor of iff it has the smallest priority among , with probability , and summing gives .
Number theory and algebra
- Bézout: is the smallest positive integer combination . Inverses exist iff .
- Fermat/Euler: for , because multiplying by permutes the units.
- Lagrange: in a group, the order of an element divides the group order. This is behind primitive root tests and order-finding.
- A polynomial of degree over a field has at most roots. That gives Schwartz-Zippel (a random evaluation catches a nonzero polynomial with probability ), the uniqueness of interpolation, and why only has roots (Miller-Rabin).
- Deterministic Miller-Rabin for 64-bit inputs with the bases is a verified computational fact, not dependent on GRH.
- Lucas: , so factors along base- digits.
Counting
- Bijection: Dyck paths, binary trees, triangulations and non-crossing matchings are all Catalan via explicit maps. The reflection principle counts paths that cross a line: reflect the prefix up to the first touch.
- Double counting: counts (set, marked element) pairs.
- Burnside: the number of orbits is the average number of fixed points, by counting pairs with two ways.
- Generating functions: sums of independent choices multiply; that’s a proof as well as an algorithm.
- Sign-reversing involutions: to show an alternating sum is small, pair objects of opposite sign and count the unpaired fixed points (inclusion-exclusion, LGV lemma).
Games
- Induct on the game tree: a position is losing iff every move goes to a winning position. Minimax is the same induction one level up: the minimax value at depth is optimal under optimal play given that the value at depth already is, bottoming out at terminal positions.
- Alpha-beta pruning doesn’t change the answer: a branch is pruned only once its value is proven to lie outside the window that the rest of the tree could possibly use, so the pruned subtree’s exact value can never affect the final minimax value returned at the root.
- Sprague-Grundy: a sum of games has Grundy value equal to the XOR of components. Proof: from XOR every move makes it nonzero; from nonzero there’s a move to (by the definition of mex).
- Strategy stealing: if the first player loses, the first player can “pass” by making a move that does no harm, then steal the second player’s strategy. That’s a contradiction, so the first player wins (Hex, Chomp). It’s non-constructive.
- Pairing / mirror strategy: pair up moves so every move has a reply that restores a symmetric or paired position.
Geometry
- Convex hull (Graham scan / Andrew’s monotone chain): maintain the invariant that the last three points on the stack form a left turn; on a right turn, pop before pushing. Proof by orientation test at every step means the output can never contain a right turn, i.e. it is convex, and the pops never discard a true hull vertex (a popped point is strictly inside the triangle formed around it).
- Rotating calipers: the antipodal-pair angle is monotone as the calipers rotate a full turn, so each edge direction is visited once and the total work across all antipodal pairs is , not .
- Sweep line: process events in sorted sweep-coordinate order and maintain the invariant that the active set is exactly the structures currently intersecting the sweep line; correctness reduces to showing every structural change (an insertion, deletion, or intersection) is itself an event, so nothing is missed between events.
Lower bounds
- Decision trees: any comparison sort has leaves, so depth . With -ary answers, queries.
- Adversary: answer queries to keep as many inputs consistent as possible. Finding the max needs comparisons (every non-max must lose once); max and min together need .
- Reductions (fine-grained): if solving fast would solve fast, and is believed hard (3SUM, APSP, OV), then so is . Recognising a 3SUM-hard or NP-hard problem tells you a constraint must be small.
- Reductions (NP-hardness): rarer in CP, but recognise the shape: a polynomial-time reduction from a known NP-hard to that preserves yes/no answers proves is NP-hard too. For optimization problems, a gap reduction (distinguishing instances with value from value ) proves hardness of approximation, not just of exactness.
Backtracking and branch and bound
Correctness needs two things: the search is exhaustive (every candidate is visited if no branch is pruned, usually by induction on the decision prefix), and every prune is safe (the bounding function is a genuine upper/lower bound on anything reachable from that branch, so discarding it can never discard the optimum). A weak bound is still correct, just slow — bound quality is a performance property, not a correctness one.
Randomised algorithms
- Las Vegas: always correct, random running time. Bound the expectation with linearity of expectation (quicksort, treaps, randomised incremental geometry via backward analysis).
- Monte Carlo: may be wrong with small probability. Repeat to amplify, and bound the error with a union bound or Chernoff: the sum of independent indicators deviates from its mean by a factor with probability .
- Hash collisions: with comparisons and a modulus , the failure probability is about . Polynomial hashes of length collide with probability per pair.
Habits
- Write a three-sentence proof sketch before coding: the invariant, the exchange, or the recurrence’s meaning.
- Prove the edge cases: , ties, empty sets, all equal.
- Keep a list of greedy counterexamples (non-canonical coin systems, earliest-start interval scheduling, densest-first knapsack). They train scepticism.
- When an editorial says “it’s easy to see”, find the lemma it skipped — that’s exactly the “why this works” paragraph to focus on, not the implementation details around it.
- Explain the proof out loud, or to a teammate. Teaching exposes the gaps a silent read-through hides.
Proof discovery
Finding why an idea works, before or instead of a formal proof.
| Step | How |
|---|---|
| guess with data | brute force small cases, look at the optimal objects, not only the values |
| strengthen the claim | a stronger statement is often easier to prove by induction (it gives a stronger hypothesis) |
| look at an optimal solution | assume an optimum that differs from your greedy at the first position; can you swap it towards yours without loss? (exchange argument) |
| find what never changes | invariants explain impossibility and reachability |
| find what always improves | potentials explain termination and amortized costs |
| attack your own claim | generate random and adversarial cases to break it; a counterexample is progress |
| prove the lower bound separately | ”at least ” (counting, adversary, invariant) plus “at most ” (construction) |
| Idea type | Proof pattern |
|---|---|
| greedy | exchange argument, staying ahead, matroid structure |
| DP | states capture everything relevant (optimal substructure); order is topological |
| DP optimization | quadrangle inequality / monotone argmin; convexity for Aliens and slope trick |
| construction | verify by a checker for all small parameters; necessary conditions match |
| data structure | invariant per node; amortized potential |
Examples of each pattern are listed in 🐬 Proof Techniques.