Use this when the complexity is right but the time limit isn’t. Fix things in this order: complexity, then memory access, then I/O, then micro-optimisation. A removed beats any amount of tuning. If you’re within about 2x of the limit, the techniques below almost always close the gap.

I/O

StepGainNote
cin.tie(nullptr)->sync_with_stdio(false)3-10xdon’t mix with scanf/printf afterwards
'\n' instead of endlhuge on big outputendl flushes; flushes cost about a second
build output in one string, print once1.5-2xfor + lines
fread + hand parser2-5x over unsynced cinonly for numbers

Rough times for reading integers: synced cin about 4 s, unsynced about 0.9 s, scanf about 1.2 s, fread parser about 0.15 s.

static char buf[1 << 25];
size_t bl = 0, bp = 0;
inline int gc() {
    if (bp == bl) { bl = fread(buf, 1, sizeof buf, stdin); bp = 0; if (!bl) return -1; }
    return buf[bp++];
}
inline ll readInt() {
    int c = gc(); while (c != '-' && (c < '0' || c > '9')) c = gc();
    bool neg = c == '-'; if (neg) c = gc();
    ll x = 0; for (; c >= '0' && c <= '9'; c = gc()) x = x * 10 + (c - '0');
    return neg ? -x : x;
}
static char obuf[1 << 25]; size_t op = 0;
inline void writeInt(ll x, char end = '\n') {
    if (x < 0) obuf[op++] = '-', x = -x;
    char t[20]; int n = 0;
    do t[n++] = '0' + x % 10, x /= 10; while (x);
    while (n) obuf[op++] = t[--n];
    obuf[op++] = end;
}
// at exit: fwrite(obuf, 1, op, stdout);

Line input: after cin >> n, call cin.ignore() (or cin >> ws) before getline, otherwise getline returns the empty rest of the line. Read until EOF with while (cin >> x).

__int128 streams:

istream& operator>>(istream& is, i128& x) { string s; is >> s; x = 0; bool neg = s[0] == '-';
    for (size_t i = neg; i < s.size(); i++) x = x * 10 + (s[i] - '0'); if (neg) x = -x; return is; }
ostream& operator<<(ostream& os, i128 x) { if (x < 0) os << '-', x = -x; string s;
    do s += char('0' + x % 10), x /= 10; while (x); return os << string(s.rbegin(), s.rend()); }

File I/O for USACO and old judges: freopen("name.in", "r", stdin); freopen("name.out", "w", stdout);, guarded by #ifndef LOCAL.

Judges that run your code on your own machine (Hacker Cup) allow solving test cases on several threads: read everything, solve with a std::thread pool pulling indices from an atomic<int>, then print in order. Never do this on Codeforces. solve() must not touch shared mutable globals when run this way — give each test its own data; only the sequential read phase may share state.

Code Jam / Kick Start / Hacker Cup also want a specific multi-test output format: print "Case #" << t << ": " before every answer, t 1-indexed.

putchar_unlocked/getchar_unlocked skip the per-call mutex that putchar/getchar acquire — safe in a single-threaded program, and roughly 2x faster than the locked versions, though fread/fwrite buffering above already captures most of that gain.

Memory access is usually the biggest win

ChangeEffect
innermost loop over the last indexup to 10x on
flatten vector<vector<T>> into one array, index i * m + jremoves a pointer chase per access
i, k, j order in matrix multiply3-5x, and lets GCC vectorise
struct of arrays instead of array of structs when only one field is hotfewer cache lines
renumber vertices in BFS/DFS order (tree problems)adjacency becomes local
int instead of ll where the range allowshalf the bandwidth
CSR graph (head, to) instead of vector<vector<int>>1.5-3x for graph traversals with edges

Cache sizes matter: L1 is about 32 KB, L2 about 256 KB-1 MB, L3 several MB. A random access into a 400 MB table is a cache miss of about 100 ns, so random accesses is 10 s, not 0.1 s.

Store a static sorted array in BFS order of the implicit complete binary search tree (node has children , ): the first few levels then share cache lines, and the descent is branch-free.

struct Eytzinger {                                           // cache-friendly lower_bound on a static sorted array
    vector<int> t; int n;
    Eytzinger(const vector<int>& sorted) : t(sz(sorted) + 1), n(sz(sorted)) { int i = 0; build(sorted, i, 1); }
    void build(const vector<int>& a, int& i, int k) { if (k <= n) { build(a, i, 2 * k); t[k] = a[i++]; build(a, i, 2 * k + 1); } }
    int lowerBound(int x) const {                            // smallest element >= x, or INT_MAX
        int k = 1;
        while (k <= n) k = 2 * k + (t[k] < x);
        k >>= __builtin_ffs(~k);
        return k ? t[k] : INT_MAX;
    }
};

Worth it only for many repeated queries on the same static array (rebuilding costs as much as one sort); for a handful of queries, plain lower_bound is simpler and the asymptotic cost is identical either way.

Expensive operations

SlowFaster
% in the inner loopif (x >= M) x -= M; after an addition (values stay below )
a * b % M many timesBarrett/Montgomery reduction (fast modmul), or accumulate in u64 and reduce rarely: for lets you add 8 products before one %
division by a runtime constantprecomputed reciprocal; the compiler does it for compile-time constants, so make MOD constexpr
map/setsorted vector + lower_bound, or arrays after compression
unordered_mapgp_hash_table, or an array if keys are bounded
vector<bool> in a hot loopvector<char> or bitset
std::functiontemplate lambda (auto&&)
endl, pow, log, sqrt on integers'\n', loops, __lg, integer sqrt
repeated push_backreserve first
deque as a stack/queuevector + head index

Branch prediction: sorted data makes if (a[i] > x) predictable, and that alone can be a 3-5x difference. Branchless forms (x += (a[i] > t) * a[i]) help when the data is random.

Pragmas

#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,popcnt,lzcnt")

These give 2-4x on tight numeric loops, bitset work and __builtin_popcount (a single instruction with popcnt). Put them before the includes. They are fine on Codeforces and AtCoder; on an unknown judge with an old CPU, avx2 can cause an illegal-instruction RE. O3 occasionally slows code down, so measure.

What GCC auto-vectorises: simple loops over contiguous arrays with no aliasing and no data-dependent control flow. An loop of a[i] = min(a[i], b[i] + c) over int with AVX2 runs about elements per instruction, so ( simple ops) can pass in 2-3 s. That is the ” with pragmas” trick on Codeforces.

Bitset: divide by 64

When an inner loop does the same boolean operation over many independent bits, pack them into words: subset sum dp |= dp << w, transitive closure, dense matching, LCS. becomes . See Bitset.

Allocation

Node-based structures (treaps, tries, persistent segment trees, map<int, vector<int>>) spend much of their time in new. Options:

  • Preallocate node pools as global arrays and use indices instead of pointers. Indices are half the size of pointers and cache better.
  • Bump allocator: override global new to bump a pointer into a static buffer, with delete a no-op. Fast, but memory is never freed, so a growing vector leaks its old buffers. Use it with reserve or only for node containers.
static char mem[450 << 20]; size_t used = 0;
void* operator new(size_t s) { s = (s + 15) & ~size_t(15); void* p = mem + used; used += s; return p; }
void operator delete(void*) noexcept {}
void operator delete(void*, size_t) noexcept {}

Algorithmic constant factors

Instead ofUse
recursive segment tree for sumsFenwick tree (about 3x faster) or bottom-up segment tree (about 2x)
set for sliding-window minmonotonic deque
sort for the -th elementnth_element
heap Dijkstra on a dense graph array Dijkstra
separate powmod inversesbatch inverse: prefix products, one inverse, walk back; or, for every inverse specifically, the sieve below
rebuilding a structure per queryreuse with rollback
long double everywheredouble where precision allows (roughly 2x faster)
vector<int> inversesUpTo(int n, int m) {                     // inv[i] = i^{-1} mod m (m prime, n < m), O(n)
    vector<int> inv(n + 1); inv[1] = 1;
    for (int i = 2; i <= n; i++) inv[i] = m - (long long)(m / i) * inv[m % i] % m;
    return inv;
}

Measure, don’t guess

auto t0 = chrono::steady_clock::now();
// ...
cerr << chrono::duration<double>(chrono::steady_clock::now() - t0).count() << "s\n";

Generate the true worst case (maximum , adversarial structure such as a path graph, a star, all-equal values, sorted input), time it locally with -O2, and assume the judge is 1-2x slower than your machine.

Sorting queries offline so consecutive ones touch nearby data (Mo’s order, Hilbert order) is as much a cache optimisation as an algorithmic one (Mo’s Algorithm).