Read the constraints, compute the real operation count, and that number tells you the intended solution.

Asymptotic notation

NotationMeaningReads as
for grows no faster than (upper bound)
for grows no slower than (lower bound)
and and grow at the same rate
is strictly smaller order than
is strictly larger order than

In competitive programming "" is almost always used loosely to mean (a tight bound, not just an upper one) — stating a true-but-weak bound like “bubble sort is ” is correct but useless, so the convention is to always give the tightest bound you can prove. and matter mainly for lower-bound arguments (e.g. “no comparison sort can be ”).

Which guarantee do you actually have, since they give different promises for the same stated bound:

GuaranteePromiseExample
Worst casetrue for every input of size quicksort is worst case
Average casetrue in expectation over a fixed distribution of inputs (usually uniform random)quicksort is on random input
Expected (randomized algorithm)true in expectation over the algorithm’s own random choices, for every inputrandomized quicksort is expected, on any input
Amortizedtrue for the total cost of operations, divided by — individual operations may be expensivevector::push_back is amortized, worst case for one call

Only worst case is safe against an adversary who sees your algorithm (adaptive interaction, anti-hash tests, a judge with hacked tests); average case needs the input to actually be drawn from the assumed distribution (an adversarial judge input breaks it, which is exactly why plain quicksort on adversarial data degrades to and randomized pivoting fixes it); expected and amortized both hold unconditionally over all inputs, but expected is a per-call promise (randomized over the algorithm) while amortized is a total-cost promise (divided evenly, no randomness required).

What fits in one second

About - simple operations per second: for vectorisable array loops, for cache-missing or branchy code, for map, modulo-heavy or long double code.

TargetTypical tools
-permutations, brute force
-, bitmask DP, SOS
submask enumeration
meet in the middle
interval DP with extra dimension
Floyd, Hungarian, interval DP
quadratic DP, Dijkstra
, Mo, sqrt decomposition, CDQ, HLD
sorting, segment tree, FFT
-, linear sieve, prefix sums, two pointers
trial division, harmonic lemma
, Du’s sieve, Min-25, Lucy
, binary exponentiation, digit DP, matrix power

Other constraint clues:

  • across tests: per-test cost must depend on that test’s , never on MAXN.
  • and both with “online” or forced decoding (x ^= lastAns): you need a persistent or online structure, not Mo or offline sorting.
  • An oddly small parameter (alphabet , , values , at most 3 special vertices) is the exponential or DP dimension.
  • A time limit of 5+ s with suggests or a bitset .
  • Memory limits below 64 MB rule out persistent structures on .

Memory

TypeElements in 256 MB
char / bool
int
long long / double
bits in a bitset
  • vector overhead is 24 bytes, so vector<vector<int>> with rows costs 24 MB plus heap headers before any data.
  • set/map nodes cost about 40-48 bytes plus the key.
  • A persistent segment tree with updates on needs about nodes. At 12 bytes each that’s 53 MB.
  • Recursion: each frame is roughly 50-200 bytes.

Useful magnitudes

QuantityValue
double exact integersup to
(fits in ll); does not
, ,
, , 78498, 664579, 50847534
max divisors of / / 240 / 1344 / 103680
max distinct prime factors, 15
prime gap below under 1500
partitions
Catalan , ,
Bell
sieve work is
at most 4 for any practical

Asymptotics in practice

  • With several parameters, keep them all: , , .
  • Common sums: , in practice, has distinct values, a loop where j *= 2 gives .
  • , so comparison sorting is . Any algorithm that could sort by comparisons inherits the bound (for example element distinctness, convex hull via the lift to a parabola).
  • Expected, amortized and worst case are different promises. All three are fine for a total time limit; only worst case is safe against adaptive interaction or persistence.

Recurrences

RecurrenceSolutionWhere
binary search
quickselect, median of medians
merge sort, D&C
CDQ with a sort or BIT
Karatsuba
Strassen
median of medians
Hanoi
van Emde Boas
outside the master theorem

Master theorem for : compare with . If is polynomially smaller, the leaves dominate and . If they are equal up to , . If is polynomially larger (and regular), .

Akra-Bazzi for uneven splits: find with ; then . Example: gives and .

Recursion tree intuition: if level costs shrink geometrically the root dominates, if they’re equal multiply by the depth, if they grow the leaves dominate.

A linear recurrence has a closed form via the characteristic polynomial’s roots; compute the -th term in with matrix power or with Bostan–Mori (Linear Recurrences).

Amortized analysis

The total cost of a sequence is bounded even if single operations are expensive.

  • Aggregate. Bound the total directly. A monotonic stack pushes each element once and pops it at most once, so a while loop that sometimes pops items is still overall.
  • Accounting. Overcharge cheap operations and save the credit. push_back with doubling: charge 3 per insert, and each element pays for its own future copy and one copy of an older element.
  • Potential. Pick with . The amortized cost is , and the sum telescopes. Binary counter: = number of ones, so an increment flipping ones has amortized cost .

The workhorse arguments:

ArgumentUsed by
each element is pushed and popped oncemonotonic stack/queue, two pointers, two-stack queue
a pointer never decreases more than it increasesprefix function, Z-function, Kasai’s LCP, Ukkonen
the smaller side moves, so each element moves timessmall-to-large, union by size, DSU on tree, HLD light edges
each merge destroys a distinct value or a nodesegment tree beats ( = number of distinct values), segment tree merging ( = number of nodes), Chtholly tree with random assigns
splay trees, link-cut trees: amortized

To prove a new bound: find what makes an operation expensive, find a non-negative quantity that the expensive step decreases, and bound how much cheap steps increase it.

When amortization breaks

  • Rollback undoes the progress that paid for the work. Rollback DSU must use union by size without path compression ( worst case).
  • Persistence lets an expensive old version be reused many times. Splay trees and doubling vectors lose their bounds.
  • Adversarial interaction with a per-query time limit can hit the worst case of every operation.

Complexity trade-offs in practice

Big-O hides constants that decide accepted vs TLE. Rough throughput on a judge (simple operations per second): additions in a tight loop ; random memory accesses ; map/set operations ; hash map operations .

ChoicePrefer when
(Mo, sqrt decomposition) vs : both fine; sqrt has better constants with simple arrays
segment tree vs FenwickFenwick when the operation is invertible: 2–3 faster, less code
set vs sorted vectorstatic data: sorted vector + lower_bound (cache-friendly)
online vs offlineoffline lets you sort queries, use CDQ, or reverse time
recursion vs iterationdepth : iterate (stack limit)
exact long double vs integerintegers whenever possible (exact and faster)
sort vs counting sortvalues up to : counting / radix

Choosing block sizes

For a sqrt decomposition with update cost and query cost , the balanced choice is ; if updates are times more expensive per element than queries, take . For Mo’s algorithm with queries, minimizes .

Profiling

Generate a max test (and adversarial ones: sorted, all equal, star graph, long path), run with time, and look at which loop dominates before optimizing anything.

Related: Complexity and Analysis, Sqrt Decomposition, Mo’s Algorithm.