计算式
C(n,m)=m!⋅(n−m)!n!此处略去
当 (a,p)=1 且 a⋅bmodp=1 时,称 b 为 amodp 的逆元。
当 bmodp 的逆元为 c 时,可得
==bamodp(bamodp)⋅((b⋅c)modp)(a⋅c)modp计算式
b 为 amodp 的乘法逆元, 记为 b=inv(a,p).
≡≡C(n,m)m!⋅(n−m)!n!n!⋅inv(m!,p)⋅inv((n−m)!,p)(modp)可用 exgcd 求逆元. 单组数据可直接算,但多组数据且范围较大时,时间消耗大,打表又消耗了大量空间,有局限性。
当 p 为素数时, ap−2=inv(a,p).
可用快速幂取模替换解法2的 exgcd,但局限性相同。
当 p 是素数时
≡⋅C(n,m)(C(nmodp,mmodp)modp)(C(pn,pm)modp)(modp)显然这是一个递归计算式,而且还是尾递归.
每次需要求解的 C(nmodp,mmodp)modp 的数据范围一定在 [0,p) 内. 当 p 不是很大 (105) 时,可以预打表,直接计算.
r=C(n,m)modp定义状态
(t,a,b)循环不变式
r=t⋅C(a,b)modp状态转移
tab⇒t⋅C(amodp,bmodp)modp⇒pa⇒pb初始状态
(1,n,m)转移后
(C(nmodp,mmodp)modp,pn,pm)由归纳法和卢卡斯定理得循环不变式成立.
如果当前状态可以直接导出 r ,则终止状态转移.
(t,a,b)r=t⋅C(a,b)modp(t=0)(a<b)(a=b)(b=0)⇒r=0⋅C(a,b)⇒r=t⋅0⇒r=t⋅1⇒r=t⋅1=0=0=t=tll lucas(const ll n, const ll m, const ll p) {
ll t = 1, a = n, b = m;
while (t && a>=b) {
if (a == b || b == 0){
return t;
}
t *= C(a % p, b % p, p);
a /= p, b /= p;
}
return 0;
}