Combinatorics: nCr, Pascal's Triangle, and Catalan Numbers

Sanjeev SharmaSanjeev Sharma
3 min read

Advertisement

Combinatorics

nCr Approaches

Approach 1: Pascal's triangle — O(n^2) space, simple for small n Approach 2: Precomputed factorials + modular inverse — O(n) space, O(1) query Approach 3: Lucas' theorem — large n with prime modulus

Python Implementation

MOD = 10**9 + 7

# Approach 1: Pascal's triangle
def pascal(n):
    C = [[0] * (n + 1) for _ in range(n + 1)]
    for i in range(n + 1):
        C[i][0] = 1
        for j in range(1, i + 1):
            C[i][j] = (C[i-1][j-1] + C[i-1][j]) % MOD
    return C

# Approach 2: Precomputed factorials
def build_comb(max_n, mod=MOD):
    fact = [1] * (max_n + 1)
    for i in range(1, max_n + 1):
        fact[i] = fact[i-1] * i % mod
    inv_fact = [1] * (max_n + 1)
    inv_fact[max_n] = pow(fact[max_n], mod - 2, mod)
    for i in range(max_n - 1, -1, -1):
        inv_fact[i] = inv_fact[i+1] * (i+1) % mod
    def C(n, k):
        if k < 0 or k > n: return 0
        return fact[n] * inv_fact[k] % mod * inv_fact[n-k] % mod
    return C

C = build_comb(10**6)
print(C(10, 3))   # 120
print(C(20, 10))  # 184756

# Catalan numbers: C(2n, n) / (n+1)
def catalan(n, C_func):
    return C_func(2*n, n) * pow(n+1, MOD-2, MOD) % MOD

# Catalan(n) counts:
# - Balanced parentheses of length 2n
# - Binary search trees with n nodes
# - Triangulations of (n+2)-gon
# - Non-crossing partitions
for i in range(8):
    print(f"Catalan({i}) = {catalan(i, C)}")
# 1, 1, 2, 5, 14, 42, 132, 429

C++ Implementation

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

const long long MOD = 1e9 + 7;

long long power(long long b, long long e, long long m = MOD) {
    long long r = 1; b %= m;
    while (e > 0) { if (e&1) r=r*b%m; b=b*b%m; e>>=1; }
    return r;
}

struct Comb {
    vector<long long> fact, inv_fact;
    Comb(int n) : fact(n+1), inv_fact(n+1) {
        fact[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i-1]*i%MOD;
        inv_fact[n] = power(fact[n], MOD-2);
        for (int i = n-1; i >= 0; i--) inv_fact[i] = inv_fact[i+1]*(i+1)%MOD;
    }
    long long C(int n, int k) {
        if (k < 0 || k > n) return 0;
        return fact[n]*inv_fact[k]%MOD*inv_fact[n-k]%MOD;
    }
    long long catalan(int n) {
        return C(2*n, n) * power(n+1, MOD-2) % MOD;
    }
};

Java Implementation

public class Combinatorics {
    static final long MOD = 1_000_000_007L;
    long[] fact, invFact;

    public Combinatorics(int n) {
        fact = new long[n + 1];
        invFact = new long[n + 1];
        fact[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i-1] * i % MOD;
        invFact[n] = power(fact[n], MOD - 2);
        for (int i = n-1; i >= 0; i--) invFact[i] = invFact[i+1] * (i+1) % MOD;
    }

    long power(long b, long e) {
        long r = 1; b %= MOD;
        while (e > 0) { if ((e&1)==1) r=r*b%MOD; b=b*b%MOD; e>>=1; }
        return r;
    }

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

LeetCode Problems

  • 62. Unique Paths — C(m+n-2, m-1)
  • 96. Unique Binary Search Trees — Catalan number
  • 1359. Count All Valid Pickup and Delivery Options — combinatorics with factorial
  • 2400. Number of Ways to Reach a Position — nCr mod prime

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro