Work in : . Addition, subtraction and multiplication commute with reduction; division by means multiplying by , which exists iff . For a prime , is a field and . GCD, CRT and linear congruences are in Congruences and CRT; Barrett / Montgomery reduction in Fast Modular Multiplication.

A modint

template <int MOD> struct Mint {                          // MOD prime (inv uses Fermat)
    int v = 0;
    Mint() {}
    Mint(ll x) { v = int(x % MOD); if (v < 0) v += MOD; }
    Mint& operator+=(Mint o) { if ((v += o.v) >= MOD) v -= MOD; return *this; }
    Mint& operator-=(Mint o) { if ((v -= o.v) < 0) v += MOD; return *this; }
    Mint& operator*=(Mint o) { v = int((ll)v * o.v % MOD); return *this; }
    Mint& operator/=(Mint o) { return *this *= o.inv(); }
    Mint pow(ll e) const { Mint r = 1, b = *this; for (; e; e >>= 1, b *= b) if (e & 1) r *= b; return r; }
    Mint inv() const { return pow(MOD - 2); }
    friend Mint operator+(Mint a, Mint b) { return a += b; }
    friend Mint operator-(Mint a, Mint b) { return a -= b; }
    friend Mint operator*(Mint a, Mint b) { return a *= b; }
    friend Mint operator/(Mint a, Mint b) { return a /= b; }
    Mint operator-() const { return Mint() - *this; }
    friend bool operator==(Mint a, Mint b) { return a.v == b.v; }
    friend bool operator!=(Mint a, Mint b) { return a.v != b.v; }
    friend ostream& operator<<(ostream& os, Mint a) { return os << a.v; }
};
using mint = Mint<998244353>;
struct Comb {                                             // factorials up to n, O(1) binomials
    vector<mint> f, fi, iv;
    Comb(int n) : f(n + 1), fi(n + 1), iv(n + 1) {
        f[0] = 1; for (int i = 1; i <= n; i++) f[i] = f[i - 1] * i;
        fi[n] = f[n].inv(); for (int i = n; i > 0; i--) fi[i - 1] = fi[i] * i;
        for (int i = 1; i <= n; i++) iv[i] = fi[i] * f[i - 1];   // 1 / i
    }
    mint C(int n, int k) const { return k < 0 || k > n ? 0 : f[n] * fi[k] * fi[n - k]; }
    mint P(int n, int k) const { return k < 0 || k > n ? 0 : f[n] * fi[n - k]; }
};
RuleDetail
negative remaindersC++ % keeps the sign of the dividend: use ((a % m) + m) % m
products(ll)a * b % m for ; (__int128)a * b % m up to
exponents for ; for composite see Euler / power towers (Number Theory Miscellany)
comparisons / orderingmeaningless after reduction: compare before reducing, or use exact arithmetic
fractions in answers”output ”: keep everything as mint
equal answersequality mod of results is evidence, not proof (random-prime hashing of exact values)

Binary exponentiation, Fermat and Euler

ll power(ll a, ll e, ll m) { ll r = 1 % m; a %= m; if (a < 0) a += m; for (; e; e >>= 1, a = (__int128)a * a % m) if (e & 1) r = (__int128)r * a % m; return r; }
ll mulSlow(ll a, ll b, ll m) { ll r = 0; a %= m; for (; b; b >>= 1, a = (a + a) % m) if (b & 1) r = (r + a) % m; return r; }   // no __int128

The same loop works in any monoid: matrices (linear recurrences), permutations (), affine maps, the “apply times” doubling of functional graphs.

Inverses in bulk

vector<ll> inversesUpTo(int n, ll p) {                    // inv[i] for 1 <= i <= n < p, O(n)
    vector<ll> inv(n + 1, 1);
    for (int i = 2; i <= n; i++) inv[i] = (p - (p / i) * inv[p % i] % p) % p;
    return inv;
}
vector<mint> batchInverse(const vector<mint>& a) {        // all a[i]^{-1} with one exponentiation (a[i] != 0)
    int n = sz(a); vector<mint> pre(n + 1, 1), res(n);
    for (int i = 0; i < n; i++) pre[i + 1] = pre[i] * a[i];
    mint suf = pre[n].inv();                              // = (a_0 ... a_{n-1})^{-1}
    for (int i = n - 1; i >= 0; i--) res[i] = suf * pre[i], suf *= a[i];
    return res;
}

Integer pitfalls

ll floorDiv(ll a, ll b) { return a / b - ((a % b != 0) && ((a < 0) != (b < 0))); }   // any signs, b != 0
ll ceilDiv(ll a, ll b) { return a / b + ((a % b != 0) && ((a < 0) == (b < 0))); }
bool mulOverflows(ll a, ll b) { ll c; return __builtin_mul_overflow(a, b, &c); }

C++ division truncates toward zero, so floor and ceiling differ from / exactly when the remainder is nonzero and the signs differ (floor) or agree (ceiling). Check products against a bound with __builtin_mul_overflow or a > lim / b instead of multiplying first.

Signed/unsigned comparison

Comparing int against unsigned (or size_t) silently converts the signed value to unsigned first — a negative int becomes a huge positive number, so -1 < sz(v) can be false. Cast explicitly, or keep indices signed throughout (the sz() macro exists for exactly this).

Unusual number representations

Negative base : every integer has a unique representation with digits and no sign. Balanced ternary: digits with weights (weighing with weights on both pans). A Trygub number stores a huge binary integer as a sparse map of signed base- digits, supporting “add ” in amortized and reading any bit or the sign.

string toNegativeBase(ll x, int b) {                       // base -b, b >= 2
    if (x == 0) return "0";
    string s; while (x != 0) { ll r = ((x % b) + b) % b; s.push_back(char('0' + r)); x = (x - r) / -b; }
    reverse(all(s)); return s;
}
ll fromNegativeBase(const string& s, int b) { ll r = 0; for (char c : s) r = r * -b + (c - '0'); return r; }
string toBalancedTernary(ll x) {                           // digits '-', '0', '+'
    if (x == 0) return "0";
    string s;
    while (x != 0) { int r = int(((x % 3) + 3) % 3); if (r == 2) s += '-', x = (x + 1) / 3; else s += r ? '+' : '0', x = (x - r) / 3; }
    reverse(all(s)); return s;
}
ll fromBalancedTernary(const string& s) { ll x = 0; for (char c : s) x = x * 3 + (c == '+') - (c == '-'); return x; }
struct TrygubNumber {                                      // value = sum digits[k] * 2^(30k), digits in (-2^30, 2^30)
    static constexpr int K = 30; static constexpr ll BASE = 1LL << K;
    map<int, ll> d;
    void add(ll x, int e) {                                // += x * 2^e, |x| < 2^31, e >= 0
        x *= 1LL << (e % K);
        for (int blk = e / K; x != 0; blk++) { ll& v = d[blk]; v += x; x = v / BASE; v -= x * BASE; if (v == 0) d.erase(blk); }
    }
    int sign() const { return d.empty() ? 0 : d.rbegin()->second > 0 ? 1 : -1; }
    int bit(int k) const {                                 // k-th bit of a non-negative value
        int blk = k / K; auto it = d.lower_bound(blk);
        ll v = it != d.end() && it->first == blk ? it->second : 0;
        if (it != d.begin() && prev(it)->second < 0) v--;  // borrow from the lower blocks
        v = ((v % BASE) + BASE) % BASE; return int(v >> (k % K) & 1);
    }
};

Sums worth knowing

SumValue
, , , ,
(); mod when the sum is
: “loop over multiples” is
,
: the sieve of Eratosthenes is
, ,
general Faulhaber / Bernoulli or Lagrange interpolation