Modular Arithmetic

intermediate30 min

Why problems ask for a remainder

When a problem says “print the answer modulo 1000000007”, it is telling you the true answer is astronomically large — often larger than any fixed-width integer can hold. Rather than asking for big-integer arithmetic, the problem asks for the remainder, which stays small.

That constant, 109+7, is prime and just under 230. Both properties matter: primality makes division possible, and the size means two values below it multiply to under 260, which fits in a long.

static final long MOD = 1_000_000_007L;

The rules that hold

Addition, subtraction, and multiplication all commute with taking the remainder. You can reduce at every step and the final answer is unchanged.

long add(long a, long b) { return (a + b) % MOD; }
long mul(long a, long b) { return (a * b) % MOD; }

Subtraction needs care because Java’s % can return a negative value.

long sub(long a, long b) { return ((a - b) % MOD + MOD) % MOD; }

Adding MOD before the second reduction forces the result non-negative. Skipping this produces a negative answer that looks almost right, which is a hard bug to spot.

Reduce as you go, never at the end

// WRONG — overflows long long before the modulo
long product = 1;
for (int i = 1; i <= n; i++) product *= i;
System.out.println(product % MOD);

// CORRECT — stays below MOD^2 at every step
long product = 1;
for (int i = 1; i <= n; i++) product = product * i % MOD;

The invariant to maintain: every value you store is already less than MOD. Then any single multiplication is at most (MOD-1)^2 ≈ 10^18, which fits in a long with room to spare.

If the modulus were larger — say near 1018 — even one multiplication would overflow, and you would need Math.multiplyHigh or BigInteger. For 109+7 you are safe.

Fast exponentiation

Computing a^b mod m by multiplying b times is O(b), which is hopeless when b is 1018. Repeated squaring does it in O(log b).

The idea: a^b is (a^(b/2))^2 when b is even, and a · a^(b-1) when odd. Reading the exponent’s bits from the bottom turns that into a loop.

static long power(long base, long exp, long mod) {
    long result = 1;
    base %= mod;
    while (exp > 0) {
        if ((exp & 1) == 1) {
            result = result * base % mod;
        }
        base = base * base % mod;
        exp >>= 1;
    }
    return result;
}

Each iteration halves the exponent, so 1018 takes about 60 steps.

Details worth checking

  • base %= mod at the start, in case the caller passes something larger.
  • exp >>= 1 on a long — using >>> is also fine here since the exponent is non-negative.
  • If the exponent can be negative, the problem means a modular inverse, not a power.
  • power(a, 0, mod) returns 1, which is correct including when a is 0 by the usual convention. If a problem cares about 00, it will say so.

Division and modular inverses

You cannot divide directly under a modulus. (a / b) % m is not ((a % m) / (b % m)) % m — integer division discards information the remainder no longer carries.

Instead multiply by the modular inverse of b: the value b⁻¹ such that b · b⁻¹ ≡ 1 (mod m).

When m is prime and b is not a multiple of it, Fermat’s little theorem gives the inverse directly:

static long inverse(long b, long mod) {
    return power(b, mod - 2, mod);        // requires mod to be prime
}

static long divide(long a, long b, long mod) {
    return a % mod * inverse(b, mod) % mod;
}

Each inverse costs O(log m). If you need many inverses of consecutive small numbers, precompute them in one linear pass rather than calling power repeatedly.

When Fermat does not apply

power(b, mod - 2, mod) is only valid when the modulus is prime. Contest problems almost always use a prime, but check.

For a composite modulus, use the extended Euclidean algorithm, which finds the inverse whenever b and m share no common factor. An inverse does not exist at all when gcd(b, m) > 1 — if a problem leads you there, the intended solution probably avoids division.

Factorials and binomial coefficients

Counting problems usually need “n choose k” modulo a prime. Precompute factorials and their inverses once, then each coefficient is O(1).

static final int MAXN = 1_000_001;
static long[] fact = new long[MAXN];
static long[] invFact = new long[MAXN];

static void precompute() {
    fact[0] = 1;
    for (int i = 1; i < MAXN; i++) {
        fact[i] = fact[i - 1] * i % MOD;
    }
    // one inverse, then walk downwards
    invFact[MAXN - 1] = power(fact[MAXN - 1], MOD - 2, MOD);
    for (int i = MAXN - 1; i > 0; i--) {
        invFact[i - 1] = invFact[i] * i % MOD;
    }
}

static long choose(int n, int k) {
    if (k < 0 || k > n) return 0;
    return fact[n] * invFact[k] % MOD * invFact[n - k] % MOD;
}

Computing the largest inverse factorial once and stepping downward avoids calling power a million times — the whole precomputation is O(N + log MOD).

The if (k < 0 || k > n) return 0; guard matters. Many recurrences naturally produce out-of-range arguments, and returning 0 for them is both mathematically right and simpler than avoiding them.

Operations under a prime modulus

OperationCodeCost
Add(a + b) % MODO(1)
Subtract((a - b) % MOD + MOD) % MODO(1)
Multiplya * b % MODO(1)
Powerpower(a, b, MOD)O(log b)
Inversepower(b, MOD - 2, MOD)O(log MOD)
Dividea * inverse(b) % MODO(log MOD)
Binomialchoose(n, k) after precomputeO(1)

Common mistakes

  • Negative results from subtraction. Always normalize.
  • Reducing only at the end, after the value already overflowed.
  • Dividing directly instead of multiplying by an inverse.
  • Using Fermat with a composite modulus.
  • int instead of long for intermediate products. Two values near 109 multiply to 1018.
  • Forgetting that the answer itself must be reduced before printing.

Practice

All answers modulo 10^9+7.

  1. Compute n! for n up to 106.
  2. Compute a^b for a and b up to 1018.
  3. Compute “n choose k” for n up to 106, answering 105 queries.
  4. Count the distinct paths from the top-left to the bottom-right of an R × C grid moving only right or down.
  5. Given N, compute the sum of 2^i for i from 0 to N, for N up to 1018.
Hints
  1. One loop, reducing each step.
  2. Fast exponentiation. Reduce a first, since it exceeds the modulus.
  3. Precompute factorials and inverse factorials, then O(1) per query.
  4. The answer is “R+C-2 choose R-1” — every path makes the same number of moves, and you only choose which are downward.
  5. The closed form is 2^(N+1) - 1. Compute the power, subtract one, and normalize the subtraction.

Related