Competitive programming: Beginner

Sefism.

Chapter 01

C++ for contests

The language features, STL containers and habits that make C++ the default contest language, plus the table that turns a constraint into the complexity you can afford.

Complexity vs n table

Also called constraints, time limit, operations per second, big-O budget

What
A rule of thumb that maps the input limit n to the slowest complexity that fits a typical one or two second time limit, assuming a judge runs very roughly 10^8 simple operations per second.
Why
The constraints are the biggest hint in the statement. Reading n first tells you which family of solutions to look for before you think about details.
Code
n up to        complexity that usually fits
10 to 11       O(n!)
18 to 22       O(2^n * n)
400 to 500     O(n^3)
2,000 to 5,000 O(n^2)
10^5 to 10^6   O(n log n) or O(n sqrt n)
10^7 to 10^8   O(n) with a small constant
10^9 and above O(log n), O(sqrt n) or a formula

Watch out

Forgetting that a sum over test cases is bounded: when the sum of n over all tests is at most 2 * 10^5, O(n log n) per test is fine.

Contest template

Also called boilerplate, starter code, bits/stdc++.h

What
The short file you start every problem from: one header that pulls in the whole standard library, a long long alias, fast input and output, and a loop over test cases.
Why
Typing the same lines under time pressure wastes minutes and invites typos. A template you know by heart lets you start on the idea straight away.
C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int t; cin >> t;
  while (t--) {
    // read one test, solve it, print the answer
  }
}

Watch out

Growing the template into pages of macros you do not understand; keep it short enough to type from memory at an ICPC site.

Fast input and output

Also called sync_with_stdio, cin.tie, fast IO, endl vs newline

What
Two lines that stop cin and cout synchronising with C input and output and flushing each other, plus printing a newline character instead of endl, which flushes on every line.
Why
With 10^6 numbers to read or print, slow IO alone can exceed the time limit on a correct solution.
C++
ios::sync_with_stdio(false);
cin.tie(nullptr);
// '\n' does not flush; endl flushes every time
for (int x : answers) cout << x << '\n';

Watch out

Mixing scanf or printf with cin or cout after turning sync off, which can print output in the wrong order.

Compiler flags and sanitizers

Also called g++, -O2, -Wall, -fsanitize, _GLIBCXX_DEBUG

What
Compile with warnings on for normal runs, and with sanitizers and the debug STL while hunting a bug, so out-of-range indexes and overflow stop the program with a message.
Why
Most wrong answers and runtime errors are an index off by one or an overflow. Sanitizers find them in seconds on your own machine instead of costing a penalty.
Shell
g++ -std=c++20 -O2 -Wall -Wextra -o a a.cpp
./a < in.txt

# while debugging
g++ -std=c++20 -g -fsanitize=address,undefined \
  -D_GLIBCXX_DEBUG -o a a.cpp

Watch out

Testing only on the sample input and never on your own edge cases, such as n = 1 or all values equal.

vector, pair and tuple

Also called dynamic array, std::vector, std::pair, structured bindings

What
A vector is a resizable array with O(1) index access and amortised O(1) push_back. A pair or tuple groups values that compare element by element, first by the first value.
Why
Almost every solution stores input in a vector, and pairs let you sort by one key and keep the original index alongside it.
C++
vector<int> a(n);
for (auto& x : a) cin >> x;
vector<pair<int, int>> byValue;
for (int i = 0; i < n; i++) byValue.push_back({a[i], i});
sort(byValue.begin(), byValue.end());
auto [value, index] = byValue[0];  // smallest value

Watch out

Declaring a vector of size n and then calling push_back n times, which leaves n zeros at the front.

Sorting with a comparator

Also called custom sort, lambda comparator, strict weak ordering

What
Passing a function to sort that returns true when the first element must come before the second, usually written as a lambda.
Why
Greedy solutions often start with an unusual order, such as by end time or by a ratio, and the comparator is where that decision lives.
C++
vector<pair<int, int>> v = {{3, 1}, {1, 2}, {3, 0}};
sort(v.begin(), v.end());  // by first, then second
sort(v.begin(), v.end(), [](auto& a, auto& b) {
  return a.second < b.second;
});

Watch out

Writing <= in a comparator: it breaks strict ordering and can crash sort or loop forever.

set, map and multiset

Also called ordered set, std::map, std::set, multiset, balanced BST

What
Sorted containers built on a balanced binary search tree, with O(log n) insert, erase and search, and lower_bound to find the first element not below a value.
Why
They answer "what is the next larger value" or "how often does x occur" while elements are added and removed, which a sorted vector cannot do cheaply.
C++
set<int> s = {5, 1, 9};
auto it = s.lower_bound(4);  // points at 5
map<string, int> cnt;
cnt["abc"]++;                // missing key starts at 0
multiset<int> ms = {3, 3, 7};
ms.erase(ms.find(3));        // removes one 3 only

Watch out

Calling ms.erase(3) on a multiset, which removes every copy of 3, or lower_bound(s.begin(), s.end(), x), which is O(n) on a set.

priority_queue

Also called heap, min-heap, max-heap

What
A heap that always gives the largest element in O(1) and inserts or removes in O(log n). Adding greater turns it into a min-heap.
Why
It drives Dijkstra, scheduling greedies and "always take the best available item" problems.
C++
priority_queue<int> maxHeap;
priority_queue<int, vector<int>, greater<int>> minHeap;
minHeap.push(5);
minHeap.push(2);
int smallest = minHeap.top();  // 2
minHeap.pop();

Watch out

Forgetting that the default is a max-heap, then getting the largest distance first in Dijkstra.

STL algorithms toolkit

Also called unique, accumulate, next_permutation, max_element, iota

What
Ready-made functions in the standard library for sorting, removing duplicates, summing, finding extremes and walking through permutations.
Why
Each one replaces a loop you might get wrong under time pressure, and they are well tested and fast.
C++
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
ll sum = accumulate(a.begin(), a.end(), 0LL);
int mx = *max_element(a.begin(), a.end());
vector<int> p(n);
iota(p.begin(), p.end(), 0);  // 0, 1, ..., n-1
do {
  // try the order in p
} while (next_permutation(p.begin(), p.end()));

Watch out

Passing 0 instead of 0LL to accumulate, which sums in int and overflows.

Integer overflow and long long

Also called overflow, long long, 64-bit, int limits

What
An int holds values up to about 2.1 * 10^9, and a long long up to about 9.2 * 10^18. Multiplying two int values overflows before the result is stored in a long long.
Why
Sums of 10^5 values up to 10^9, or products of two values up to 10^5, already exceed int. Overflow gives silent wrong answers, not errors.
C++
int a = 100000, b = 100000;
long long bad = a * b;        // overflows first
long long good = 1LL * a * b; // 10000000000

Watch out

Using int for sums and answers "because the input fits in int".

01 · C++ for contests1 / 11