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)
Mathematical Proof
If is prime and , Fermat’s Little Theorem states:
Multiplying both sides by gives
So raising to the power (via binary exponentiation) directly yields its inverse — no division needed.
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 .
Mathematical Proof
By Bézout’s identity, the extended Euclidean algorithm finds integers such that
If , this becomes , i.e. . Therefore (reduced mod ) is the modular inverse of .
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 .
Mathematical Proof
Write . Taking this equation mod gives
Multiply both sides by and rearrange to isolate :
Since , its inverse was already computed in an earlier iteration, so the recurrence can be built bottom-up.
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 piteratively from toMAXN - 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
| Modulus | Notes |
|---|---|
1000000007 | Standard prime modulus for most problems |
998244353 | Prime modulus commonly used for NTT |
1000000009 | Another commonly used prime |