Lucas Theorem and Its Extended Application

Lucas Theorem

Mathematical Statement

Given prime $p$, the following congruence holds:

$$\binom{n}{m} \equiv \binom{\lfloor n/p \rfloor}{\lfloor m/p \rfloor} \cdot \binom{n \bmod p}{m \bmod p} \pmod{p}$$

Proof Foundation

Lemma 1

For prime $p$:

$$\binom{p}{k} \equiv 0 \pmod{p} \text{ when } 0 < k < p$$

The numerator contains factor $p$, making the expression divisible by $p$ unless $k = 0$ or $k = p$.

Lemma 2

Using binomial theorem:

$$(x + y)^p \equiv x^p + y^p \pmod{p}$$

This follows from Fermat's Little Theorem where $a^p \equiv a \pmod{p}$ for prime $p$.

Complete Proof

Consider $(1 + x)^n$. The coefficient of $x^m$ is $\binom{n}{m}$.

By Lemma 2:

$$(1 + x)^n \equiv (1 + x^p)^{\lfloor n/p \rfloor} \cdot (1 + x)^{n \bmod p} \pmod{p}$$

To obtain term $x^m$ where $m = qp + r$:

  • Select $q$ terms of $x^p$ from $(1 + x^p)^{\lfloor n/p \rfloor}$
  • Select $r$ terms of $x$ from $(1 + x)^{n \bmod p}$

Thus coefficient becomes:

$$\binom{\lfloor n/p \rfloor}{q} \cdot \binom{n \bmod p}{r} \equiv \binom{\lfloor n/p \rfloor}{\lfloor m/p \rfloor} \cdot \binom{n \bmod p}{m \bmod p} \pmod{p}$$

Implementation Example

#include <iostream>
using namespace std;

const int MAX_PRIME = 100005;
typedef long long int64;

int64 factorial[MAX_PRIME], inverse[MAX_PRIME];

int64 combination(int64 n, int64 k, int64 mod) {
    if (k > n) return 0;
    return factorial[n] * inverse[k] % mod * inverse[n - k] % mod;
}

int64 lucas(int64 n, int64 k, int64 mod) {
    if (k == 0) return 1;
    return combination(n % mod, k % mod, mod) * lucas(n / mod, k / mod, mod) % mod;
}

void preprocess(int64 mod) {
    factorial[0] = factorial[1] = 1;
    inverse[0] = inverse[1] = 1;
    
    for (int i = 2; i <= mod; i++)
        factorial[i] = factorial[i-1] * i % mod;
        
    for (int i = 2; i <= mod; i++)
        inverse[i] = (mod - mod/i) * inverse[mod % i] % mod;
        
    for (int i = 2; i <= mod; i++)
        inverse[i] = inverse[i-1] * inverse[i] % mod;
}

int main() {
    int test_cases, n, m;
    int64 prime_mod;
    
    cin >> test_cases;
    while (test_cases--) {
        cin >> n >> m >> prime_mod;
        preprocess(prime_mod);
        cout << lucas(n + m, n, prime_mod) << endl;
    }
    return 0;
}

Extended Lucas Theorem

Problem Definition

Compute $\binom{n}{m} \bmod p$ where $p$ is not necessarily prime.

Solution Approach

Decompose $p$ into prime power factors: $p = \prod p_i^{a_i}$.

Since these factors are pairwise coprime, compute $\binom{n}{m} \bmod p_i^{a_i}$ separately and combine using Chinese Remainder Theorem.

Express binomial coefficient as:

$$\binom{n}{m} = \frac{n!}{m!(n-m)!}$$

Factor out powers of prime $q$ from factorial:

$$n! = q^{\lfloor n/q \rfloor} \cdot \lfloor n/q \rfloor! \cdot \prod_{q \nmid i, i \leq n} i$$

Components:

  • $q^{\lfloor n/q \rfloor}$ computed via fast exponentiation
  • $\lfloor n/q \rfloor!$ computed recursively
  • Remaining product has periodicity modulo $q^{a_i}$

Periodic Product Computation

For modulus $q^{a}$, the product $\prod_{q \nmid i}^n i$ exhibits cycles of length less then $q^{a}$.

Example with $n = 19, q = 3, a = 2$:

$$19! \bmod 9 = (1 \times 2 \times 4 \times 5 \times 7 \times 8)^2 \times 19 \times 3^6 \times 6! \pmmod{9}$$

First cycle computed explicitly, remaining cycles handled via exponentiation.

Implementation

#include <bits/stdc++.h>
using namespace std;

typedef long long int64;

int64 extended_gcd(int64 a, int64 b, int64 &x, int64 &y) {
    if (b == 0) {
        x = 1; y = 0;
        return a;
    }
    int64 result = extended_gcd(b, a % b, x, y);
    tie(x, y) = make_pair(y, x - (a/b) * y);
    return result;
}

int64 modular_inverse(int64 value, int64 mod) {
    int64 x, y;
    extended_gcd(value, mod, x, y);
    return (x % mod + mod) % mod;
}

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

int64 factorial_mod(int64 n, int64 prime, int64 power) {
    if (n == 0) return 1;
    
    int64 cycle_result = 1;
    for (int i = 2; i <= power; i++)
        if (i % prime != 0)
            cycle_result = cycle_result * i % power;
            
    cycle_result = fast_power(cycle_result, n / power, power);
    
    for (int i = 2; i <= n % power; i++)
        if (i % prime != 0)
            cycle_result = cycle_result * i % power;
            
    return cycle_result * factorial_mod(n / prime, prime, power) % power;
}

int64 combination_prime_power(int64 n, int64 m, int64 prime, int64 power) {
    if (n < m) return 0;
    
    int64 numerator = factorial_mod(n, prime, power);
    int64 denominator_m = factorial_mod(m, prime, power);
    int64 denominator_nm = factorial_mod(n - m, prime, power);
    
    int64 prime_count = 0;
    for (int64 i = prime; i <= n; i *= prime) prime_count += n / i;
    for (int64 i = prime; i <= m; i *= prime) prime_count -= m / i;
    for (int64 i = prime; i <= n - m; i *= prime) prime_count -= (n - m) / i;
    
    return numerator * modular_inverse(denominator_m, power) % power * 
           modular_inverse(denominator_nm, power) % power * 
           fast_power(prime, prime_count, power) % power;
}

int64 chinese_remainder_combine(int64 residue, int64 mod_component) {
    static int64 total_mod;
    return residue * (total_mod / mod_component) % total_mod * 
           modular_inverse(total_mod / mod_component, mod_component) % total_mod;
}

int64 extended_lucas(int64 n, int64 m, int64 composite_mod) {
    int64 temp = composite_mod, result = 0;
    chinese_remainder_combine(0, 0); // Initialize static variable
    
    for (int64 factor = 2; factor * factor <= temp; factor++) {
        if (temp % factor == 0) {
            int64 prime_power = 1;
            while (temp % factor == 0) {
                prime_power *= factor;
                temp /= factor;
            }
            result = (result + chinese_remainder_combine(
                combination_prime_power(n, m, factor, prime_power), 
                prime_power)) % composite_mod;
        }
    }
    
    if (temp > 1)
        result = (result + chinese_remainder_combine(
            combination_prime_power(n, m, temp, temp), 
            temp)) % composite_mod;
            
    return result;
}

int main() {
    int64 n, m, mod;
    cin >> n >> m >> mod;
    cout << extended_lucas(n, m, mod) << endl;
    return 0;
}

Tags: number-theory combinatorics modular-arithmetic lucas-theorem chinese-remainder-theorem

Posted on Tue, 12 May 2026 18:23:17 +0000 by Moneypenny