See Getting Started to learn how to setup your environment and walk your first steps in the competitive programming world!
- Competition Basics
- C++ Basics
- Basic Datastructures
- Algorithm Design
- Sort & Search
- Graphs
- Dynamic Programming
- Advanced Datastructures
- Strings
- Geometry
- Mathematics
g++ -std=c++20 -O2 -Wall program.cpp -o program
./program < input.txt
#include "bits/stdc++.h"
using namespace std;
#define ll long long
Inside main function, always add:
ios::sync_with_stdio(false);
cin.tie(nullptr);
Typical technique | Time complexity | Safe max |
---|---|---|
Binary exponentiation, binary search, GCD | ||
Trial division, sieve of Eratosthenes | ||
Linear scan, two-pointer, cumulative sums | ||
Sorting (merge sort, quicksort, heapsort) | ||
BFS / DFS on graph |
||
Dijkstra / Prim (binary heap) | ||
Segment tree / Fenwick tree (updates + queries) | ||
Mo’s algorithm, block decomposition | ||
DP on pairs, prefix tables, double loop | ||
Floyd–Warshall, cubic DP, matrix multiplication | ||
Bitmask DP / enumerate all subsets | ||
Backtracking all permutations |
- Really make sure you fully read the problem description, especially to discover properties which limit possible result cases
cin >>
reads whole string till spacecin.get(c)
readschar c
, also spaces and\n
getline(cin, s)
reads whole line asstring s
vector<char> S(tmp.begin(), tmp.end())
seperates string into char vector
for &
):
int n;
cin >> n;
vector<int> A(n);
for (int &x : A) {
cin >> x;
}
cout <<
standard(statement ? "true" : "false")
to output a value depending on the bool value of thestatement
- Round Up in fractions, use
a+b-1/b
instead ofa/b
. - Square to avoid
sqrt
iota(v.begin(), v.end(), 0); // 0,1,2,...
accumulate(v.begin(), v.end(), 0LL); // sum
sort(v.begin(), v.end(), greater<>()); // descending
equal(v.begin(), v.begin()+n/2, v.rbegin()); // palindrome
Type | Size / Range | Notable properties |
---|---|---|
int |
|
fastest arithmetic, default loop index |
long long |
64-bit signed, safe for most sums/products | |
unsigned int |
wrap-around mod |
|
unsigned long long |
64-bit unsigned, modular hashes | |
double |
|
hardware FPU; geometry, probabilistic DP |
long double (GCC) |
|
extra precision for numeric analysis |
char |
tiny integer; ASCII grids, bit tricks | |
bool |
vector<bool> ) |
logical flags, visited arrays |
size_t |
unsigned type returned by sizeof , container sizes |
Container / Helper | Key operations & complexity | Typical contest use |
---|---|---|
vector<T> |
Dynamic array, index/push_back |
Arrays, graphs, DP tables |
deque<T> |
Push/pop front/back |
Sliding window, 0-1 BFS |
list<T> |
Insert/erase |
Rarely used; custom linked lists |
array<T,N> |
Fixed-size array, index |
Small static arrays, DP, geometry |
stack<T> |
LIFO push/pop/top |
DFS, parentheses checker |
queue<T> |
FIFO push/pop/front |
BFS, task scheduling |
priority_queue<T> |
Push/pop/top |
Dijkstra, K-largest elements |
set<T> /multiset<T>
|
Insert/erase/find |
Ordered sets, coordinate compression |
map<K,V> /multimap<K,V>
|
Insert/erase/find |
Ordered maps, frequency counts |
unordered_set<T> /unordered_multiset<T>
|
Insert/erase/find |
Fast lookup, frequency counts |
unordered_map<K,V> /unordered_multimap<K,V>
|
Insert/erase/find |
Fast maps, hash tables |
bitset<N> |
Bitwise ops |
Bitmask DP, subset problems |
string |
Access |
Token parsing, hashing, string algorithms |
pair<A,B> /tuple<...>
|
Structured grouping, lex compare | Edges, multi-value returns |
vector
,array
,queue
,dequeue
,stack
,bitset<N>
- talking about iterators
- talking about strings
priority_queue
Based on Balanced BST.
set
- typical
set.insert()
, `` - we can only operate on iterators through
set.begin()
/set.end()
,next(iterator,steps)
- also
set.find(element)
gives back an iterator. Check if the element has been found viaiterator != set.end()
- to traverse use
for (auto it = set.begin(); it != set.end(); it++){ cout << *it; }
- since sets only store unique values
set.size()
gives back the count of distinct values
- typical
map
unordered_set
,unordered_map
pair
,tuple
,optional
,variant
- Decision: (True/False)
- Search / Construct (Valid Solution / Structure)
- Counting (Number of Solutions)
- Enumeration (All Solutions)
- Optimization (Best Value)
- Approximation (Near Optimal)
- Interactive (Answering Queries)
Try all possibilities directly.
- Guaranteed correct if implemented correctly
- Almost always too slow in runtime
Make locally optimal decisions, which (hopefully) also lead to global optimum.
- Fast and simple when valid
- Incorrect for many types of problems
- Requires justification through invariant or exchange argument
Split problem into disjoint subproblems, solve each recursively, then merge results.
- Reduces complexity by splitting up search space
Break a problem into overlapping subproblems, cache results and then reuse them.
- Top-down: recursion + memoization.
- Bottom-up: iterative filling of a table
- Requires state definition and transitions.
Rephrase the problem so known structures apply.
- Take datastructure and construct it in a fitting way
- Reduce problem to easier known problem
- Examples: Graphs, Union-Find, Fenwick, Compression
First you need to sort your container via
sort(vector.begin(), vector.end())
most basic ASC sortsort(vector.begin(), vector.end(), greater<ll>());
most basic DESC sort
Then you can search for elements via
Function | Returns | Meaning |
---|---|---|
binary_search(first, last, val) |
true val exists in [first, last) . |
Existence test in sorted range |
lower_bound(first, last, val) |
Iterator to first element ≥ val (or last if none). |
First element of the equal-or-greater range |
upper_bound(first, last, val) |
Iterator to first element > val (or first if none). |
End of equal range |
equal_range(first, last, val) |
Pair of iterators [lower_bound, upper_bound) . |
Range of elements equal to val
|
If you want Floor (largest
auto it = A.upper_bound(x);
if (it == A.begin()) // implies there are no values <= x in A
else { --it; cout << *it; } // it is largest value <= x in A
If you want Ceil (smallest
auto it = A.lower_bound(x);
if (it == A.end()) // implies there are no values >= x in A
else { cout << *it; } // it is smallest value >= x in A
.front()
returns reference to first element.begin()
returns iterator to first element (you want to use for iterations)
Search for biggest element in sorted list. Split up search space each round.
int binary_search_idx(vector<int>& A, int target) {
int low = 0;
int high = A.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2; // overflow safe
if (A[mid] == target) return mid; // succesfuly found target
if (A[mid] < target) low = mid + 1; // go right
else high = mid - 1; // go left
}
return -1; // not in A
}
-
Description:
$n \times n$ Matrice.$A_{i,j}=1$ means there is an edge from vertice$i$ to$j$ -
Space:
$O(n^2)$ -
Count Neighbours:
$O(n)$ -
Test Edge:
$O(1)$ -
Implementation:
vector<vector<bool>> A(n, vector<bool>(n, false))
-
Description: List with
$n$ Entries, each having an array containing all vertices it's connected to. -
Space:
$O(n+m)$ -
Count Neighbours:
$O(|\text{neighbours}|)$ -
Test Edge:
$O(|\text{neighbours}|)$ -
Implementation:
vector<vector<int>> adjlist
, for weightedvector<vector<pair<int,ll>>> adj
- Description: List of all Edges displayed as a pair of the connected vertices
-
Space:
$O(m)$ -
Count Neighbours:
$O(m)$ -
Test Edge:
$O(m)$ -
Implementation:
vector<pair<int,int>> edges
- Purpose: Find shortest paths in unweighted Graph (and discover connectivity by layers)
-
Description: Level‐by‐level (breadth‐first) exploration from the start vertex
$s$ -
Runtime:
$O(n+m)$ -
Input: Adjacency List
vector<vector<int>> &adjlist
, Starting Vertices
- Output:
- Code:
int n = adjlist.size();
queue<int> q;
q.push(s);
vector<int> dist(n, INF), parent(n, -1);
dist[s] = 0;
parent[s] = -1;
while (!q.empty()) {
int v = q.front();
q.pop();
for (int w : adjlist[v]) {
if (dist[w] == INF) {
dist[w] = dist[v] + 1;
parent[w] = v;
q.push(w);
}
}
}
and for the output:
for (int w = 0; w < n; ++w) {
if (parent[w] != -1)
cout << parent[w] << " --> " << w << " (dist=" << dist[w] << ")\n";
}
- Purpose: Detect connectivity, reachability, cycles. Construct Paths. Perform topological sorting. Solve backtracking problems.
-
Description: Explores Graph greedy from starting vertice
$s$ -
Runtime:
$O(n+m)$ -
Input: Adjacency List
vector<vector<int>> &adjlist
, Starting Vertices
- Output: DFS Tree
- Code:
void visit(int v, vector<bool> &visited, vector<int> &parent) {
visited[v] = true;
for (int w : adjlist[v]) {
if (!visited[w]) {
parent[w] = v;
visit(w, visited, parent);
}
}
}
to kick of the search and get the output:
vector<bool> visited(n, false);
vector<int> parent(n, -1);
visit(s, visited, parent); // s = Startknoten
for (int w = 0; w < n; ++w) {
if (parent[w] != -1)
cout << parent[w] << " --> " << w << "\n";
}
- Purpose: Find all shortest Paths in a (weighted) Graph
- Description: Iterates over all vertice combinations
-
Runtime:
$O(n^3)$ - Input: Adjacency Matrice
-
Output: 2D Array
mat
wheremat[i][j]
= shortest distance from vertice$i$ to$j$ - Code:
// Initialize: mat [i][j] = infty
for (k = 0; k < n; k++) {
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
if (mat[i][k] + mat[k][j] < mat[i][j]) {
mat[i][j] = mat[i][k] + mat[k][j];
}
}
}
}
- Purpose: Compute shortest paths from a single source to every other vertex in a non-negatively weighted graph
- Description: Use a min-heap to repeatedly extract the closest vertex and relax its outgoing edges until all shortest distances from the source are determined.
-
Runtime:
$O(m \log n)$ -
Input: Adjacency List
vector<vector<pair<int,int>>> &adj
, Starting Vertices
-
Output: Vector with shortest distance from
$s$ to every vertex$v$ vector<ll> dist(n)
, optionally alsovector<int> prev(n)
for the reconstructed path - Code:
vector<ll> dijkstra(const vector<vector<pair<int,int>>>& adj, int s, vector<int>* parent = nullptr) {
int n = adj.size();
vector<ll> dist(n, INF);
if (parent) parent->assign(n, -1);
using State = pair<ll,int>;
priority_queue<State, vector<State>, greater<State>> pq;
dist[s] = 0;
pq.emplace(0, s);
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (d != dist[u]) continue;
for (auto [v, w] : adj[u]) {
ll alt = d + w;
if (alt < dist[v]) {
dist[v] = alt;
if (parent) (*parent)[v] = u;
pq.emplace(alt, v);
}
}
}
return dist;
}
When you have many queries on the same graph, pay the
vector<vector<ll>> buildAllPairs(const vector<vector<pair<int,int>>>& adj) {
int n = adj.size();
vector<vector<ll>> D(n, vector<ll>(n, INF));
for (int s = 0; s < n; ++s) {
D[s][s] = 0;
D[s] = dijkstra(adj, s);
}
return D;
}
Given:
-
$G={1, \dots, n}$ Objects ($1 \leq |G| \leq 10^3$ ) -
$C \in \mathbb{N}$ Capacity ($1 \leq C \leq 10^4$ ) -
$v: G \to \mathbb{N}$ Value ($1 \leq v_i \leq 10^9$ ) -
$w: G \to \mathbb{N}$ Weight ($1 \leq w_i \leq 10^9$ )
Question:
- What is the max total value of unique objects, which total weight is smaller than the capacity?
Sub-Solution:
-
$F_{i,j}$ is max value of first$i$ objects with$j$ capacity - We are looking for
$F_{n,C}$ - Define Base Cases:
$\forall j: F_{0,j}=0$ and$\forall i: F_{i,0}=0$ - Define Recurrence: For
$i,j \geq 1$ is $$ F_{i,j} = \begin{cases} F_{i-1,j} & w_i > j\[4pt] \max\bigl(F_{i-1,j},; F_{i-1,,j-w_i}+v_i\bigr) & \text{else} \end{cases} $$
DP Solution:
for(int64_t i = 1; i <= n; ++i)
for(int64_t j = 1; j <= C; ++j) {
f[i][j] = f[i-1][j];
if (j >= w[i])
f[i][j] = max(f[i - 1][j], f[i-1][j-w[i]] + v[i]);
}
- Never use
float
- Prefer
long long
for exact integer geometry - If you absolutely need non-integral values, use
long double
Treat any
long long EPS = 1e-9;
int sgn(long double x) {
if (x > EPS) return +1; // significantly positive
if (x < -EPS) return -1; // significantly negative
return 0; // close enough to zero
}
Useful for comparisons with tolerance
Point (Vector) most robust:
struct P {
long long x, y;
P operator+(P o)const{return {x+o.x,y+o.y};}
P operator-(P o)const{return {x-o.x,y-o.y};}
P operator*(double k)const{return {x*k,y*k};}
};
via this all operations are already available:
using P = complex<long double>;
where
P p(x,y);
long double x = p.real(), y = p.imag();
Squared Distance
long long sqdist(P a, P b) {
}
Dot Product (scalar product)
double dot(P a,P b){
return a.x*b.x + a.y*b.y;
}
Cross Product (vector product)
long long cross(P a, P b, P c){
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
Counter Clockwise
int ccw(P a, P b, P c){
return sgn(cross(b-a,c-a));
}
-
$>0$ : left turn (counter clockwise) -
$=0$ : collinear -
$<0$ : right turn (clockwise)
Segment two endpoints
Line two different points
Projection of point onto line
reflection across line
Distance point→line and point→segment
- Input: Segments
ab
andcd
- Task: Decide if
ab
andcd
cut each other - Observation: If they cut,
c
andd
have to be on different sites ofab
(and other way around)
bool intersect (P a, P b, P c, P d) {
if (ccw(a, b, c) == 0 && ccw(a, b, d) == 0) {
if (sqdist(a, b) < sqdist(c, d)) {
swap(a, c), swap(b, d);
}
int64_t dab = sqdist(a, b);
return (sqdist(a, c) <= dab && sqdist(b, c) <= dab) ||
(sqdist(a, d) <= dab && sqdist(b, d) <= dab);
}
return ccw(a, b, c) * ccw(a, b, d) <= 0 &&
ccw(c, d, a) * ccw(c, d, b) <= 0;
}
...
Area & perimeter
Point-in-polygon (winding vs. ray-cast)
Convex vs. non-convex
- Input: Set of points
vector <pt> &pts
- Task: Find smallest konvex polygon which holds
pts
- Observation: Counter clockwise only Linksknicke
- Andrews Algorithm: Calculate upper and lower hull seperately by sorting each lexicographically, adding the next point and deleting all previous points which create a Rechtsknick
vector<P> hull(vector<P> &pts) {
sort(pts.begin(), pts.end());
auto half_hull = [](auto begin, auto end) {
vector<P> res(end - begin); // max size of hull
int64_t k = 0; // real size of hull
for (auto it = begin; it != end; ++it) {
while (k >= 2 && ccw(res[k - 2], res[k - 1], *it) <= 0) --k;
res[k++] = *it;
}
res.resize(k);
return res;
};
vector<P> lower = half_hull(pts.begin(), pts.end()); // forward
vector<P> upper = half_hull(pts.rbegin(), pts.rend()); // backwards
if (lower.size() == 1) { // edge case: single point
return lower;
}
lower.insert(lower.end(), next(upper.begin()), prev(upper.end()));
return lower;
}
- Input: Set of points
vector <pt> &pts
- Task: Find the two closest points
{p1, p2}
- Process: Iterate over the set, updating the shortest currently known distance
d
by observing sweeps of widthd
and heightd*2
if any of them are closer thand
pair<P, P> closest_pair(vector<P> &pts)
{
sort(pts.begin(), pts.end(), comp_x);
set<P, bool (*)(P, P)> sweep({*pts.begin()}, comp_y);
long double opt = INF;
P p1, p2;
for (size_t left = 0, right = 1; right < pts.size(); ++right)
{
while (pts[right].x - pts[left].x >= opt) {
sweep.erase(pts[left++]);
}
auto lower = sweep.lower_bound(P{-INF, pts[right].y - opt});
auto upper = sweep.upper_bound(P{-INF, pts[right].y + opt});
for (auto it = lower; it != upper; ++it) {
long double d = dist(pts[right], *it);
if (d < opt) {
opt = d;
p1 = *it;
p2 = pts[right];
}
sweep.insert(pts[right]);
}
}
return {p1, p2};
}
struct circ { P x; long long r; };
Circle–point tests, tangents from a point
Line–circle intersection
Circle–circle intersection & common tangents
Power of a point, radical axis
Area by cross product
Incenter, circumcenter, centroid, orthocenter
Voronoi diagram ...