当 p 为质数,(1 le m le n) 时,求组合数(C_{n}^{m} mod{p})。
Lucas定理
对于质数 p, 有:
[egin{aligned}
C_{n}^{m} equiv C_{n / p}^{m / p} cdot C_{n mod{p}}^{m mod {p}} pmod{p}
end{aligned}
]
其中(n / p) 和 (m / p) 为整除。
证明:
引理1:
[egin{aligned}
C_{p}^{i} equiv frac{p}{i} cdot C_{p - 1}^{i - 1} equiv 0 pmod{p}
end{aligned}
]
引理1证明:
[egin{aligned}
C_{p}^{i} = frac{p!}{i! cdot (p - i)!} = frac{p}{i} cdot frac{(p - 1)}{(i - 1)! cdot (p - i)!}
end{aligned}
]
得证。
引理2:
[egin{aligned}
(1 + x)^p equiv 1 + x^p pmod{p}
end{aligned}
]
引理2证明:(二项式定理)
[egin{aligned}
(1 + x)^p = C_p^0 + C_p^1 cdot x + ... + C_p^p cdot x^p
end{aligned}
]
从第二项到倒数第二项都可以在模 p 意义下消掉,只剩下第一项和最后一项,得证。
Lucas定理:
令 (n = s cdot p + q, m = t cdot p + r), 则(s = left lfloor frac{m}{p}
ight
floor, t = left lfloor frac{m}{p}
ight
floor)。
[egin{aligned}
(1 + x)^n = [(1 + x)^p]^s cdot (1 + x)^q equiv (1 + x^p) ^ s cdot (1 + x) ^ q equiv sum_{i = 0}^{s} (C_s^i cdot x^{ip}) cdot sum_{j = 0} ^ q (C_{q}^{j} cdot x^j) pmod{p} qquad (1)
end{aligned}
]
又因为:
[egin{aligned}
(1 + x) ^ n = (1 + x)^{sp + q} = sum_{k = 0} ^ {sp + q} C_{sp + q} ^ {k} cdot x^k qquad (2)
end{aligned}
]
因为 ((1) equiv (2) pmod{p}) , 对比其中的 (x^{tp + r})项:
[egin{aligned}
C_{sp + 1} ^ {tp + r} cdot x ^ {tp + r} equiv C_s^t cdot x^{tp} cdot C_q^ r cdot x^r pmod{p}
end{aligned}
]
[egin{aligned}
C_{sp + 1} ^ {tp + r} equiv C_s^t cdot C_q^ r pmod{p}
end{aligned}
]
[egin{aligned}
C_{n}^{m} equiv C_{n / p}^{m / p} cdot C_{n mod{p}}^{m mod {p}} pmod{p}
end{aligned}
]
得证。
于是可以递归求解。
cpp:
lld powe(lld a, lld b, lld p) {
lld base = 1;
while(b) {
if(b & 1) base = (base * a) % p;
a = (a * a) % p; b >>= 1;
}
return base;
}
lld comb(lld n, lld m, lld p) {
if(n < m) return 0;
if(n == m) return 1;
if(m > n - n) m = n - m;
lld cn = 1, cm = 1;
for(lld i = 0; i < m; i++) {
cn = (cn * (n - i)) % p;
cm = (cm * (i + 1)) % p;
}
return (cn * powe(cm, p - 2, p)) % p;
}
lld lucas(lld n, lld m, lld p) {
lld ans = 1;
while(n && m && ans) {
ans = (ans * comb(n % p, m % p, p)) % p;
n /= p;
m /= p;
}
return ans % p;
}
int main() {
int T; scanf("%d", &T);
while(T--) {
lld n, m, p;
scanf("%lld%lld%lld", &n, &m, &p);
printf("%lld
", lucas(n, m, p));
}
return 0;
}