Day 012-Q3 — 競技数学(高度整数論・組み合わせ論)

2026-04-25 赤色 / Phase 8 ★★★★★★★★ 二項係数 mod p

問題

素数 $p$($10^9 \le p \le 2 \times 10^9$)と整数 $N, K$ が与えられる($N < p$)。二項係数 $C(N, K) \bmod p$ を求めよ。$p$ は固定の素数ではないことに注意。

制約

$0 \le K \le N \le 10^{18}$
$10^9 \le p \le 2 \times 10^9$(素数)
$N < p$ 保証

入出力例

入力例 1

10 3 1000000007

出力例 1

120

ヒント (段階的開示)

ヒント1: 方向性
$N < p$ なら Lucas 不要。フェルマー小定理で逆元を作り、$C(N,K) = N!/(K!(N-K)!)$ を計算。$N$ が $10^{18}$ なので $N!$ は不可能。
ヒント2: アプローチ
$C(N,K) = N(N-1)\cdots(N-K+1)/K!$。対称性 $C(N,K)=C(N,N-K)$ で $\min(K, N-K)$ 個の積に。
ヒント3: 誘導
def modinv(a, p):
    return pow(a, p - 2, p)

def comb_mod(n, k, p):
    if k > n - k: k = n - k
    num = 1
    for i in range(k):
        num = num * ((n - i) % p) % p
    den = 1
    for i in range(1, k + 1):
        den = den * i % p
    return num * modinv(den, p) % p

模範解答 (Python)

import sys
input = sys.stdin.readline

def modinv(a, p):
    return pow(a, p - 2, p)

def comb_mod(n, k, p):
    if k < 0 or k > n:
        return 0
    if k > n - k:
        k = n - k
    num = 1
    for i in range(k):
        num = num * ((n - i) % p) % p
    den = 1
    for i in range(1, k + 1):
        den = den * (i % p) % p
    return num * modinv(den, p) % p

line = input().split()
N, K, p = int(line[0]), int(line[1]), int(line[2])
print(comb_mod(N, K, p))

Step-by-Step 解説

1二項係数の変形
$C(N,K) = N(N-1)\cdots(N-K+1)/K!$(分子は K 個の積)。
2対称性
$C(N,K)=C(N,N-K)$。$K > N/2$ なら置換して分子の積を減らす。
3モジュラ逆元
$K!^{-1} \equiv (K!)^{p-2} \pmod{p}$(フェルマー小定理、$N < p$ から $K! \not\equiv 0$)。
4計算量
$O(\min(K, N-K))$。

よくあるミス

ミス原因正しい書き方
(n-i) % p 忘れ中間値が p より大きいnum * ((n - i) % p) % p
K ≥ p で K! ≡ 0逆元なしN < p の制約を確認
対称性を使わないK が大きいまま loopif k > n-k: k = n-k

次のステップ

  • Lucas の定理(N ≥ p の場合)
  • Andrew Granville の方法(p が素数でない場合)

自己評価

自分の回答

気づき・メモ