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.
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 formulaWatch 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 longalias, 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.
#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
cinandcoutsynchronising with C input and output and flushing each other, plus printing a newline character instead ofendl, 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.
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.
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.cppWatch 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
vectoris a resizable array with O(1) index access and amortised O(1)push_back. Apairortuplegroups 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.
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 valueWatch 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
sortthat 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.
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_boundto 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.
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 onlyWatch 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
greaterturns it into a min-heap. - Why
- It drives Dijkstra, scheduling greedies and "always take the best available item" problems.
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.
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
intholds values up to about 2.1 * 10^9, and along longup to about 9.2 * 10^18. Multiplying twointvalues overflows before the result is stored in along 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.
int a = 100000, b = 100000;
long long bad = a * b; // overflows first
long long good = 1LL * a * b; // 10000000000Watch out
Using int for sums and answers "because the input fits in int".
unordered_map and anti-hash tests
Also called hash map, custom hash, splitmix64, anti-hash
- What
- A hash map with average O(1) operations. With the default hash, people can craft inputs where every key collides, making it O(n) per operation.
- Why
- On platforms where other contestants can hack or add tests, a plain
unordered_mapcan time out. A randomised hash or amapavoids it.
struct SafeHash {
size_t operator()(uint64_t x) const {
static const uint64_t seed =
chrono::steady_clock::now().time_since_epoch().count();
x += seed + 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
};
unordered_map<long long, int, SafeHash> cnt;Watch out
Assuming unordered_map is always faster than map; with small n or hostile tests it can be slower.