When numbers become very large, we perform computations modulo a fixed integer , storing only the remainder. Two integers are congruent modulo if

Basic Operations

Division is not defined directly. Instead,

where is the modular multiplicative inverse of , computed with the methods below.

Modular Exponentiation

Computing efficiently is a prerequisite for most of this note — see Binary Exponentiation for the modpow implementation. Every function below assumes modpow(a, n, mod) is available.

Modular Inverse

The modular inverse of modulo is the number satisfying

It exists iff .

Fermat’s Little Theorem (Prime Modulus)

long long modinv(long long a, long long mod) {
    return modpow(a, mod - 2, mod); // mod must be prime
}

Time Complexity:
Space Complexity:

Extended Euclidean Algorithm (Any Modulus)

Works even when is not prime, as long as .

long long extgcd(long long a, long long b, long long &x, long long &y) {
    if (b == 0) { x = 1; y = 0; return a; }
    long long x1, y1;
    long long g = extgcd(b, a % b, x1, y1);
    x = y1;
    y = x1 - (a / b) * y1;
    return g;
}
 
long long modinv(long long a, long long mod) {
    long long x, y;
    long long g = extgcd(a, mod, x, y);
    if (g != 1) return -1; // inverse doesn't exist
    return ((x % mod) + mod) % mod;
}

Algorithm:

  • Run the extended Euclidean algorithm on to obtain along with coefficients .
  • If the gcd isn’t , no inverse exists.
  • Otherwise, reduce modulo (adding if negative) to get .

Time Complexity:
Space Complexity: (recursion stack)

Inverses of in

Useful when every inverse from to is needed (e.g. for factorial tables), since calling modinv times costs .

vector<long long> inverseRange(int n, long long mod) {
    vector<long long> inv(n + 1);
    inv[1] = 1;
    for (int i = 2; i <= n; i++)
        inv[i] = (mod - (mod / i) * inv[mod % i] % mod) % mod;
    return inv;
}

Time Complexity:
Space Complexity:

Chinese Remainder Theorem (CRT)

Solves a system of congruences with pairwise coprime moduli:

A unique solution exists modulo :

long long crt(long long r1, long long m1, long long r2, long long m2) {
    long long M = m1 * m2;
    long long inv1 = modinv(m2 % m1, m1); // extended-Euclid version, since m1 may not be prime
    long long inv2 = modinv(m1 % m2, m2);
    long long x = (r1 % M) * m2 % M * inv1 % M;
    x = (x + (r2 % M) * m1 % M * inv2 % M) % M;
    return (x + M) % M;
}

Algorithm:

  • Compute .
  • Find the inverse of mod , and the inverse of mod , using the extended Euclidean algorithm.
  • Combine the two congruences into a single residue mod using the formula above.
  • For more than two congruences, merge them pairwise, folding the result into a running pair.

Time Complexity: per merge
Space Complexity:

Combinatorics Under a Prime Modulus

Precomputing factorials and inverse factorials gives queries for .

const int MAXN = 200005;
long long fact[MAXN], invfact[MAXN];
 
void precomputeFactorials(long long mod) {
    fact[0] = 1;
    for (int i = 1; i < MAXN; i++)
        fact[i] = fact[i - 1] * i % mod;
 
    invfact[MAXN - 1] = modpow(fact[MAXN - 1], mod - 2, mod);
    for (int i = MAXN - 2; i >= 0; i--)
        invfact[i] = invfact[i + 1] * (i + 1) % mod;
}
 
long long nCr(int n, int r, long long mod) {
    if (r < 0 || r > n) return 0;
    return fact[n] * invfact[r] % mod * invfact[n - r] % mod;
}

Algorithm:

  • Build fact[i] = i! mod p iteratively from to MAXN - 1.
  • Compute invfact[MAXN - 1] once via Fermat’s Little Theorem, then fill the rest backwards using , avoiding work per entry.
  • Answer each query in using the precomputed arrays.

Time Complexity: precompute, per query
Space Complexity:

Lucas’ Theorem

Computes when can be far larger than , but itself is a small prime (too small to precompute factorials up to ).

where are the base- digits of and .

long long smallNCr(long long n, long long r, long long p) {
    if (r < 0 || r > n) return 0;
    long long res = 1;
    for (long long i = 0; i < r; i++)
        res = res * ((n - i) % p) % p * modinv((i + 1) % p, p) % p;
    return res;
}
 
long long lucas(long long n, long long r, long long p) {
    if (r == 0) return 1;
    return smallNCr(n % p, r % p, p) * lucas(n / p, r / p, p) % p;
}

Algorithm:

  • Peel off the last base- digit of and , compute directly (both digits are , so this is a small, direct computation).
  • Recurse on and to handle the remaining digits.
  • Multiply all digit-wise binomial coefficients together mod ; if any , the whole product is .

Time Complexity:
Space Complexity: (recursion stack)

Common Moduli

ModulusNotes
1000000007Standard prime modulus for most problems
998244353Prime modulus commonly used for NTT
1000000009Another commonly used prime