A working toolkit for hard problems: what to look at, which reformulations to try, and how to turn a statement into a known model. Constraint sizes are in Complexity.

The diagnostic questions

  1. What does brute force enumerate? That is the object you are optimising over. Which part of the enumeration is redundant?
  2. What is the answer’s type? Count, optimum, existence, construction, or -th object. Each has its own toolset (DP or combinatorics, greedy or flow, invariants, constructions, or digit/order-statistic search).
  3. Is there structure? Sorted, monotone, convex, tree, DAG, bipartite, planar, interval, permutation, grid, functional graph.
  4. Is there independence? Bits, primes, connected components, coordinates, or rows and columns that can be solved separately.
  5. Are the queries offline? Then you can sort them, reverse time, divide and conquer, or use Mo’s algorithm.
  6. Is it minimax or maximin? Binary search the answer.
  7. Is the general version NP-hard? Then some constraint is small. Find it: it’s the exponent or the DP dimension.
  8. What does an optimal solution look like? Prove structure lemmas: “WLOG the chosen elements are contiguous after sorting”, “at most one item is partially used”, “some optimal path uses at most turns”, “the answer is one of candidates”.

Statement phrase to tool

The statement saysTry
minimise the maximum / maximise the minimumbinary search the answer
maximise a ratio binary search , test (Dinkelbach converges faster)
number of subarrays with propertyprefix sums + hashing, two pointers, contribution with a monotonic stack
sum over all subarrays/pairs/subsets of contribution technique
exactly / at least one of eachinclusion-exclusion, Möbius over subsets
choose exactly things, cost convex in Aliens trick
range update / range querydifference arrays, BIT, lazy segment tree
connect / merge / “are together”DSU; offline deletions reverse time or segment tree on time
shortest path, weights 0/1 / small integers / non-negative / negative0-1 BFS / Dial / Dijkstra / Bellman-Ford, Johnson
difference constraints shortest paths
dependencies, prerequisitestopological sort, DAG DP
two choices per item, pairwise conflicts2-SAT
pick a closed set (taking forces )max closure via min cut
assign A to Bmatching, Hungarian, min-cost flow
disjoint paths, capacities, “each used at most times”max flow, vertex splitting
tree path / subtree queriesLCA, Euler tour, HLD, centroid decomposition
count pairs of tree vertices with distance propertycentroid decomposition, small-to-large
many patterns in a textAho-Corasick; distinct substrings suffix automaton or array
palindromesManacher, eertree, palindromic series
, huge, smallLucas, Granville for prime powers
count pairs with for all FFT/NTT
// over subsetsFWHT, SOS DP, linear basis
linear recurrence, huge matrix power, Kitamasa, Bostan-Mori, Berlekamp-Massey
both players play optimallySprague-Grundy, minimax DP, strategy stealing, pairing
expected valuelinearity of expectation, indicator variables
numbers in with a digit propertydigit DP
online, with forced decoding (^ lastans)persistent structures, online sqrt
and a weird operationsqrt decomposition, bitset, small/large threshold

Constraint to technique

The constraints are the strongest hint the setter gives — read them first. (See also Complexity and Analysis and Before Contest for the same lookup in other shapes.)

ConstraintConsider
brute force, all permutations, backtracking
bitmask DP
meet in the middle
, or flow
— Floyd-Warshall, interval DP, matrix ops
DP
— Mo’s, sqrt decomposition
— sorting, segment tree, Dijkstra
or with a small constant
, careful I/O
math, binary exponentiation, matrix power, digit DP
values sieve, frequency array, smallest prime factor
values Miller-Rabin, Pollard’s rho
two small parameters ( big, )FPT — exponential in
sum of over all tests the per-test complexity may depend on that , not the single-test bound

Reformulations

OriginalTry
a condition on all pairssort first. It often becomes a condition on adjacent elements
choose a subsetsort by a key and DP over the order (exchange argument decides the key)
a hard constraintbinary search on it, or relax it with a Lagrange multiplier
a process of operationsfind what is invariant, and characterise reachable states
operations applied forwards branch a lotwork backwards
deletionsreverse time and turn them into insertions
Manhattan distancerotate: turns it into Chebyshev distance, so axes become independent
median map to by comparing with , then require sum ; binary search
average subtract , then require sum
strictly increasing makes it non-decreasing (proof below)
productslogarithms, or prime exponent vectors
XOR along tree pathsroot prefix XORs:
bracket sequences prefix sums: never negative, end at 0 (proof below)
tree paths and subtreesEuler tour turns subtrees into ranges, HLD turns paths into ranges
interval containment or overlappoints in the plane and dominance counting
cyclic arraysdouble the array, or fix the cut point
counting configurations up to rotationBurnside
the answer is smalliterate over the answer and check feasibility
answer is a sum of independent partssolve each bit, prime or component separately
cost depends on (number of items) and (their sum)swap the DP’s value and dimension
maximum over of something convexternary search or a monotone pointer over

Modeling

SignalModel
minimum moves between configurationsBFS on the state graph
moves with costsDijkstra on the state graph, possibly with potentials
choices with overlapping subproblemsDP over (position, small extra state)
conflicts between two groupsbipartite graph: matching, König, vertex cover
selection with dependenciesmin cut (project selection)
“every row and column exactly once”bipartite perfect matching / permutation
grid with domino or rook placementsrows and columns or black and white cells as the two sides
DAG path cover maximum matching on the split graph
equivalences, mergingDSU, possibly with parity or weights
order constraintsDAG, or difference constraints if they’re numeric

Flow-modeling specifics, once “Modeling” above has pointed at flow:

  • Source left side with capacity = supply, right side sink with capacity = demand.
  • Vertex capacities: split into .
  • Lower bounds: circulation with demands.
  • Costs: min cost flow. Convex costs mean parallel edges with increasing costs.
  • Min cut = minimum “price” to separate. Infinite edges encode “cannot cut here”.

State graph: a state is everything that determines the future, such as (cell, collected keys as a mask, direction, time mod , fuel). Count states times transitions before coding, then shrink by dropping irrelevant information or exploiting symmetry.

DP state design: describe in words; the state must determine the future without the history; the transition is the last (or first) decision. When the state space is too big, look for dominance (keep a Pareto front), monotonicity of the optimal choice (DP optimisations), or swap a dimension with the value. More in DP Foundations.

Algorithmic paradigms

A paradigm is a reusable high-level strategy, distinct from any one algorithm (Quicksort is an algorithm; divide and conquer is the paradigm behind it). Paradigms are not mutually exclusive — Kruskal’s MST is greedy + DSU; A* is state-space search + a heuristic. Decide by asking: does the problem have optimal substructure? Do subproblems overlap or stay independent? Does a locally optimal choice provably compose into a globally optimal one? Is it NP-hard (then exact-small/approximate/heuristic)? Online or offline? In memory or streamed?

ParadigmIdea and mechanismIconic problemsWhen / trade-off
Brute forceenumerate every candidate, test each; correctness from exhaustiveness alonecorrectness baseline for stress testssmall only; trivial to implement, scales catastrophically
BacktrackingDFS over partial solutions, prune a branch the moment it’s invalidN-Queens, Sudoku, graph colouringreal pruning (ordering, constraint propagation) often turns exponential into fast in practice
Branch and boundbacktracking + a bound on the best achievable value from a partial solution, prune anything that can’t beat the incumbentTSP exact, 0/1 knapsack exactworst case exponential; a weak bound degenerates to brute force
Meet in the middlesplit input in half, enumerate each half, combine (sort + binary search / two pointers)subset sum , 4-SUMneeds the two halves to combine independently; extra space for the enumerated half
State-space searchmodel as states/transitions, search start goalpathfinding, 15-puzzle, Rubik’sstate explosion is the enemy; A*/IDDFS/bidirectional mitigate it
Bitmask enumerationsubsets/states as integers, __builtin_popcount, submask iteration (sub-1)&mask in totalHeld-Karp TSP, SOS DP, assignmentexponential by construction; the point is a fast constant where “better” asymptotics are overkill at this

Divide-based

ParadigmIdea and mechanismIconic problemsWhen / trade-off
Divide and conquersplit into independent subproblems, recurse, combine; , master theoremmerge sort, closest pair, Karatsuba, FFTsubproblems must genuinely not share state, else redundant recomputation
Decrease and conquerreduce to one smaller instance (not several)binary search, insertion sort, Euclid’s algorithmsimpler recurrence, less parallelizable (one active thread)
Transform and conquerchange representation/domain so the transformed problem is easierheapify, coordinate compression, Gaussian eliminationtransform cost must be amortized by the savings; must be answer-preserving

Optimization

ParadigmIdea and mechanismIconic problemsWhen / trade-off
Greedylocally optimal choice every step, correctness via exchange argument or matroid structureactivity selection, Huffman, Kruskal/Prim, Dijkstra, fractional knapsackfast when it works, silently wrong when the greedy-choice property fails (0/1 knapsack)
Matroid-based greedygreedy is provably optimal exactly when the structure is a matroidMST (graphic matroid), max weight forest, deadline scheduling (transversal matroid)recognising matroid structure is itself nontrivial
Dynamic programmingoptimal substructure + overlapping subproblems, solve each state onceknapsack, LCS, edit distance, digit/bitmask/tree DPstate design is the crux; space often reducible to the last 1-2 layers
Memoizationthe top-down form of DP: natural recursion + cacheFibonacci, tree DPgood when only a sparse fraction of states are reached, or bottom-up order is awkward
Network flowmodel as capacitated flow, exploit max-flow/min-cutbipartite matching, project selection, segmentation, disjoint pathsthe hard part is almost always the modeling, not running Dinic’s

Approximate and probabilistic

ParadigmIdea and mechanismIconic problemsWhen / trade-off
Randomizedrandom choices beat adversarial inputs or simplify design; Las Vegas (always correct, random time) vs. Monte Carlo (bounded time, random correctness)randomized quicksort, Miller-Rabin, Karger’s min-cuterror must be quantified/amplified; see Complexity Theory for BPP/RP/ZPP
Approximationtrade guaranteed optimality for a provable ratio in poly timevertex cover (2-approx), metric TSP (3/2, Christofides), set cover (), max cut (0.878, Goemans-Williamson)designing a good ratio is often as hard as the problem itself; see Complexity Theory for PTAS/FPTAS and inapproximability
Heuristic / metaheuristicfind good-not-necessarily-optimal solutions fast, no guarantees: hill climbing, simulated annealing, genetic algorithms, tabu searchlarge TSP, VLSI placement, hyperparameter tuningtuning (cooling schedule, population, mutation rate) is itself an engineering problem
Parameterized (FPT)isolate parameter , solve in : kernelization, bounded search trees, colour-coding, treewidth DPvertex cover , -path, feedback vertex setnot every NP-hard problem is FPT (W-hierarchy); can be astronomical even with a “polynomial” exponent

Query and range

ParadigmIdea and mechanismIconic problemsWhen / trade-off
Searchnarrow a candidate range repeatedly: binary (sorted), ternary (unimodal), interpolation (uniform, expected)find an element, convex minimum, “smallest with predicate()“needs sortedness or unimodality
Sliding window / two pointersmaintain a window, adjust boundaries incrementally instead of recomputinglongest substring without repeats, min window substringneeds the monotonicity assumption (shrinking never needs to re-grow past where it was)
Sqrt decomposition / Mo’sblock the array ( per op) or block the query set (sort by block, amortized total)range sum/min with updates, offline distinct-in-range, range mode loses to structures where those apply, but Mo’s handles query types (distinct count) a segment tree can’t; strictly offline
Persistent structureskeep every past version queryable (partial) or even editable (full), via path copying: new nodes per update-th smallest in a range, version-controlled arrays, “value at time “extra space per update; harder to implement correctly
Amortized / incrementalbound the total cost of a sequence, not each operation: aggregate analysis, accounting method, potential methoddynamic array push_back, DSU with compression, splay treestotal-cost guarantee, not a per-operation one — unsuitable for hard real-time constraints

Data-scale

ParadigmIdea and mechanismIconic problemsWhen / trade-off
Onlinecommit to each decision immediately, no future knowledge; measured by competitive ratio vs. optimal offlineLRU/FIFO paging, online bipartite matching, ski rentaloptimality fundamentally capped by lack of foresight
Streamingone pass, memory far smaller than the stream: reservoir sampling, Count-Min, HyperLogLog, Bloom filtersheavy hitters, distinct-IP countingapproximate with quantified error; data can’t be revisited — see Complexity Theory for communication complexity
Sublinear / property testingdecide “has property” vs. “-far from it” examining a sublinear (sometimes ) fraction of inputis this list sorted?, is this graph bipartite?no guarantee near the boundary; needs a well-defined distance metric
Cache-oblivious / external memoryefficient under a two-level memory hierarchy, either by counting block transfers explicitly or (more elegantly) with no knowledge of block size at allexternal merge sort, cache-oblivious B-treesexternal-memory needs hardware tuning (); cache-oblivious avoids that but is harder to design
Distributed / parallelsplit computation across cores (parallel) or machines (distributed): SIMD/GPU, task-parallel, MapReduce, consensus protocolsparallel prefix sum, distributed shortest paths, large-scale ML trainingAmdahl’s law bounds speedup by the sequential fraction; communication/load-balancing/fault-tolerance add complexity

Small cases and OEIS

For constructive, counting and ad hoc problems, brute force and look at the output.

  • Print the solutions themselves, not just yes/no: the structure of the construction (alternating, mirrored, extending the case, using the bits of ) is usually visible right away.
  • For integer sequences, compute 8-12 terms and search OEIS. It’s allowed, fast, and often the intended path.
  • For a polynomial in , interpolate. For a linear recurrence, run Berlekamp-Massey on terms. For periodicity, look at the answers mod small and at differences.
  • Verify every conjecture against the brute force on a wider range before submitting — ten thousand random small tests take seconds and catch nearly every wrong conjecture (Debugging and Stress Testing). Skipping this step is where wrong answers come from.
SequenceProbably
1, 2, 4, 8, 16
1, 1, 2, 3, 5, 8Fibonacci
1, 1, 2, 5, 14, 42Catalan
1, 2, 5, 15, 52Bell
1, 3, 13, 75, 541ordered Bell (Fubini)
0, 1, 2, 9, 44, 265derangements
1, 1, 3, 15, 105double factorials
1, 1, 2, 4, 9, 21, 51Motzkin
1, 2, 6, 22, 90large Schröder
1, 1, 1, 2, 3, 6, 11, 23Wedderburn-Etherington (unlabelled binary trees)
1, 1, 2, 4, 9, 20, 48unlabelled rooted trees
1, 1, 2, 3, 5, 7, 11, 15, 22partitions
: 1, 1, 3, 16, 125Cayley, labelled trees

Working backwards

Start from the final state and undo operations. This helps when the forward process branches but the backward one is forced.

  • The last operation is visible: the last stamp is fully visible, the last merge involves the maximum, the last move in a game reaches a terminal position (retrograde analysis).
  • Euclid-like moves reverse to repeated subtraction, which is the Euclidean algorithm and takes with division.
  • Reaching from with and : backwards, halve when even, otherwise subtract.
  • Edge deletions with connectivity queries: reverse time so deletions become DSU unions.
  • If an operation loses information (integer division, max), going backwards branches. Then check the backward greedy step is really forced.

Case analysis and normalisation

  • Split by size (small separately by brute force), parity, sign or zero, extremes (all equal, sorted, impossible), relative order, and structure (tree vs has a cycle, connected vs not).
  • Normalise to cut cases: WLOG (swap), the first element is positive (negate), the string starts with 0 (flip), the target is in the first quadrant (reflect), the minimum is 0 (shift).
  • Make sure the cases cover everything and agree on their boundaries. Too many cases is a smell: look for a uniform invariant or formula.

Symmetry

  • Reverse the array or rotate the grid four times and reuse the same code for each direction.
  • Assume , double the count, and fix the diagonal (counted twice).
  • In probability, symmetric items are equally likely to be first, last or in any position. Element is a prefix maximum of a random permutation with probability , so the expected number of records is .
  • In games, the mirror strategy on a symmetric position wins for the second player if mirrored moves are always legal.
  • Counting up to relabelling or rotation: Burnside and Pólya (Burnside).

Relaxation, bounds and tightness

Drop a constraint, solve the easier problem, and check or repair.

  • Bound and construct: prove a lower bound (each element needs one operation, each move decreases the potential by at most 1, a cut argument), then build a solution meeting it. Many constructive problems are exactly this.
  • LP relaxation is integral for totally unimodular systems (bipartite matching, flows, interval matrices), so the LP dual gives min-max theorems: König, max-flow/min-cut, Dilworth.
  • Lagrangian relaxation: move “exactly ” into the cost with a penalty (Aliens trick).
  • Allow illegal moves (negative counts, teleports), find the easy answer, then prove it’s achievable.

When stuck

  1. Re-read the statement and list every constraint. The unused one is usually the key.
  2. Solve tiny cases by hand or by brute force.
  3. Reverse the process, complement the count, or dualise the optimisation.
  4. Look for an invariant or a parity obstruction (Invariants).
  5. Fix one variable (the maximum, the number of groups, the first position, the answer’s bit) and optimise the rest.
  6. Solve the version with no updates, one query, a path instead of a tree, or distinct values, then add the missing piece.
  7. Think about a square-root threshold: few heavy objects plus many light ones, each easy separately.