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
- Compile:
g++ -std=gnu++20 -O2 -Wall -Wextra -pipe -g -fsanitize=address,undefined program.cpp -o program - Run:
./program < input.txt
If you want to add debug the code via debug(...) usages in the program.cpp, just add -DDEBUG to Compile Call
#include "bits/stdc++.h"
using namespace std;
#define ll long longInside 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\ngetline(cin, s)reads whole line asstring svector<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;
}put this at the top
freopen("problemname.in", "r", stdin);
freopen("problemname.out", "w", stdout);cout <<standard(statement ? "true" : "false")to output a value depending on the bool value of thestatement
- Round Up in fractions, use
a+b-1/binstead 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
A.erase(unique(A.begin(), A.end()), A.end()); // remove duplicates from vector| 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 |
When you need to process a lot (
- Determine amount of queries and prepare vectors outside of main function (e.g.
max_val=100005for$1 \leq Q \leq 10^5$ queries.) -
Collect keys which are decisive. Like
landrfor range queries$[l,r)$ in an Indices ArrayI. -
Compress via sorting + removing duplicates from
Iand implementing agetIndex(x)=lower_bound(I.begin(), I.end(), x) - I.begin()function. - Build Segment Difference Array
difffor queries which update the values: (1-indexed)diff[getIndex(l)+1] += v;and where it endsdiff[getIndex(r)+1] -= v - Determine Segment Values via
val[i] = val[i-1] + diff[i] - When wanting to answer queries over different segments, you also need to save width of each segment
widths[i+1] = I[i+1] - I[i] - Use that for example for Prefix sums
pre[i] = pre[i-1] + val[i] * widths[i]
| 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
When to use: Many queries on existence or threshold in a specific range
Compute nearest constraint in
Here prevGreater for storing index j of previous element a[j] > a[i]
// prevGreater[i]: last j<i with a[j] > a[i], else -1
vector<int> prevGreater(int n, const vector<int>& a) {
vector<int> st, L(n, -1);
for (int i = 0; i < n; i++) {
while (!st.empty() && a[st.back()] <= a[i]) st.pop_back();
L[i] = st.empty() ? -1 : st.back();
st.push_back(i);
}
return L;
}
// nextSmaller[i]: first j>i with a[j] < a[i], else n
vector<int> nextSmaller(int n, const vector<int>& a) {
vector<int> st, R(n, n);
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && a[st.back()] >= a[i]) st.pop_back();
R[i] = st.empty() ? n : st.back();
st.push_back(i);
}
return R;
}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 AIf 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)
To implement you have to go through these steps:
-
Monotonicity Check: Is there a YES/NO question about value
mwhich is also the same for all valuesx > morx < m -
Search Space: Define range
$[l,r]$ of possible answers (what is the smallest, what is the largest) -
Shrink Search Space via assigning new values to
$l$ and$r$ :
- Find minimum(first) value
xsuch thatcondition(x)is TRUE (Lower Bound):
ll ans=-1; // if no element with true for condition is found
ll l=0,r=n-1;
while (l <= r) {
ll m = l+(r-l)/2;
if (condition(m)) {
ans = mid;
r = m-1; // shrink space to [l, m-1]
} else {
l = m+1; // shrink space to [m+1, r]
}
}
return ans;- Find maximum(last) value
xsuch thatcondition(x)is TRUE (Upper Bound):
ll ans=-1; // if no element with true for condition is found
ll l=0,r=n-1;
while (l <= r) {
ll m = l+(r-l)/2;
if (condition(m)) {
ans = mid;
l = m+1; // shrink space to [m+1, r]
} else {
r = m-1; // shrink space to [l, m-1]
}
}
return ans;- Find peak value
xin an array that increases and then decreases:
while (l < r) {
ll m = l+(r-l)/2;
if (A[m] < A[m+1]) {
l = m+1; // we are ascending - shrink space to [m+1, r]
} else {
r = m; // we are descending - shrink space to [l, m]
}
}
return r;We want to find out the sum of a specific A. The naive approach would be to calculate it for each of our
The solution is Prefix Sums. We calculate on the fly for each element prefix[i]=A[0]+ ... + A[i-1]. Runtime is
Building: vector<ll> prefix(n+1,0) with prefix[0]=0 and then iterate with an index, e.g.
for (int i = 0; i < n; ++i) prefix[i+1] = prefix[i] + A[i];
To then get the value of interval (subarray
In general an interval is open, so exclusive with ( and closed, so inclusive with ].
So all intervals for a
-
$[l,r)$ =>sum = prefix[r] - prefix[l](recommended) (non empty$\iff r > l$ ) -
$[l,r]$ =>sum = prefix[r+1] - prefix[l](non empty$\iff r \geq l$ ; requires$r < n$ ) -
$(l,r)$ =>sum = prefix[r] - prefix[l+1](non empty$\iff r > l+1$ ) -
$(l,r]$ =>sum = prefix[r+1] - prefix[l+1](non empty$\iff r > l$ )
- Count amount of subarrays with specific property => use map/vector of counts
- Subarrays which
sum==x: keep mapcnt[pref], scanpref+=a[i], results inres+=cnt[pref-x]andcnt[pref]++ - Subarrays which
sum%x==0: bucket by remaining valuer=(pref % x + x) % x, results inres+=seen[r]andseen[r]++
- Subarrays which
- Max length subarray => store earliest+latest index per key
- divisibility, modulo
exists a witness inside [l, r] that the property needed is not met
-
Description:
$n \times n$ Matrix.$A_{i,j}=1$ means there is an edge from vertex$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 vertexs - Output:
- Code:
vector<int> dist(n, -1), parent(n, -1);
queue<int> q;
dist[s] = 0;
q.push(s);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : adj[u]) {
if (dist[v] == -1) { // Unvisited
dist[v] = dist[u] + 1;
parent[v] = u; // path reconstruction
q.push(v);
}
}
}- Purpose: Detect connectivity, reachability, cycles. Construct Paths. Perform topological sorting. Solve backtracking problems.
-
Description: Explores Graph greedy from starting vertex
$s$ -
Runtime:
$O(n+m)$ -
Input: Adjacency List
vector<vector<int>> &adjlist, Starting vertexs - Output: DFS Tree
- Code:
vector<bool> visited(n, false);
vector<int> parent(n, -1);
auto dfs = [&](auto &&self, int u) -> void {
visited[u] = true;
for (int v : adj[u]) {
if (!visited[v]) {
parent[v] = u;
self(self, v);
}
}
};
dfs(dfs, s);to get the output:
for (int w = 0; w < n; ++w) {
if (parent[w] != -1)
cout << parent[w] << " --> " << w << "\n";
}Graph
So we can just start at any node and test if we can reach all other nodes (for example with DFS).
Graph
Graph
- 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 vertexs -
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:
const ll INF = 1e18;
vector<ll> dist(n, INF);
priority_queue<pair<ll,int>, vector<pair<ll,int>>, greater<>> pq; // Min-priority queue storing {distance, node}
dist[s] = 0;
pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (d > dist[u]) continue; // Skip outdated states
for (auto [v, w] : adj[u]) { // w is weight
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}All-Pairs Helper, efficient for sparse graphs:
vector<vector<ll>> buildAllPairs(const vector<vector<pair<int,int>>>& adj) {
int n = adj.size();
vector<vector<ll>> D(n);
for (int s = 0; s < n; ++s) {
D[s] = dijkstra(adj, s);
}
return D;
}-
Purpose: Shortest paths from source
$s$ in graph with negative weights (detects negative cycles). -
Runtime:
$O(n\cdot m)$ -
Input: Edge List
struct Edge { int u, v, w; } - Code:
struct Edge { int u, v, w; };
vector<Edge> edges;
vector<ll> dist(n, INF);
dist[s] = 0;
for (int i = 0; i < n - 1; ++i) { // Relax all edges n-1 times
for (auto& e : edges) {
if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v])
dist[e.v] = dist[e.u] + e.w;
}
}
for (auto& e : edges) { // Run one more time to detect negative cycle
if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v]) {
cout << "Negative Cycle Detected!" << endl;
dist[e.v] = -INF; // Mark as arbitrarily small
}
}- Purpose: Find all shortest Paths in a (weighted) Graph
- Description: Iterates over all vertex combinations
-
Runtime:
$O(n^3)$ - Input: Adjacency Matrix
-
Output: 2D Array
matwheremat[i][j]= shortest distance from vertex$i$ to$j$ - Code:
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) { // Check if path through k is shorter
if (d[i][k] < INF && d[k][j] < INF) // check to avoid overflow
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}Tree = connected, acyclic graph with
- removing any edge splits it into two components
- Adding any edge creates a cycle
-
Leaf = node
$v$ with$\deg(v)=1$ -
Root = node
$r$ with no parent - Children = lower neighbors of a node
- Parent = upper neighbor of a node
- each node acts as root of a subtree
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 longfor 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
abandcd - Task: Decide if
abandcdcut each other - Observation: If they cut,
canddhave 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
dby observing sweeps of widthdand heightd*2if 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 ...