求组合数模某个数的余数。注意一般的阶乘预处理组合数,虽然也能取模,如果出现的组合数 C ( n , m ) C(n,m) C(n,m),预处理复杂度就是 O ( n ) O(n) O(n)的,如果我们的组合数很大就完了
这时就需要lucas定理了,内容是这样,
第一项可以继续用lucas递归处理,第二项的系数都是模p之后的,所以我们预处理组合数,可以只用求出来不超过p的,预处理复杂度变成
O
(
p
)
O(p)
O(p)了,会快很多。然后lucas本身的复杂度,由于一直在除
p
p
p递归,显然只有
O
(
l
o
g
n
)
O(logn)
O(logn),相比
O
(
n
)
O(n)
O(n)预处理,在
n
=
1
e
9
n=1e9
n=1e9甚至更大时显然快了很多,这就是lucas的优点
当然这也要求了模数 p p p不能太大,如果也 p = 1 e 9 p=1e9 p=1e9就没意义了
void solve(){
int n,m,p;
cin>>n>>m>>p;
vi fa(p+1);
fa[0]=1;
rep(i,1,p){
fa[i]=fa[i-1]*i%p;
}
auto C=[&](int n,int m,int p)->int{
return fa[n]*power(fa[m],p-2,p)%p*power(fa[n-m],p-2,p)%p;
};
auto &&lucas=[&](auto &&lucas,int n,int m,int p)->int{
if(m==0)return 1;
return C(n%p,m%p,p)*lucas(lucas,n/p,m/p,p)%p;
};
cout<<lucas(lucas,n+m,m,p)<<'\n';
}
可以看到Lucas也要预处理组合数,这里需要注意的是,由于p不大,可能出现阶乘的值是p的倍数,然后求逆元可能就会失败,所以我们不用求 i f a c ifac ifac了,只用求 f a c fac fac,需要逆元的时候用快速幂就行了
然后这里其实有一个推论,算组合数奇偶性的时候,可以直接`n&k==k,等价于 C ( n , k ) 模 2 = 1 C(n,k)模2=1 C(n,k)模2=1