简介
Lucas定理用于解决数据较大时的组合数学问题。其中C(n,m)mod p中的模数p为素数。
公式
C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p 。
就是不断递归求C(n%p,m%p)%p ,每次更新n=n/p,m=m/p。C(n,m)%p=(n!/m!*(n-m)!),这里的除法需要转换为乘法逆元来计算。
算法实现
逆元的计算这里用的是费马小定理算快速幂,也可用拓展欧几里得算法。
所有的乘法都是用快速乘算的。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int maxn=107;
int p[maxn];
int n,m,mod;
void init()//计算阶乘p[i]=i!
{
p[1]=1;
for(int i=2;i<=n;i++)
{
p[i]=(p[i-1]*i)%mod;
}
}
int mod_mul(int x,int n)//快速乘,就是return x*n
{
int res=0;
while(n)
{
if(n&1)
res=(res+x)%mod;
x=(x+x)%mod;
n>>=1;
}
return res;
}
int mod_pow(int x,int n)//快速幂,return x^n
{
int res=1;
while(n)
{
if(n&1)
res=mod_mul(res,x);
x=mod_mul(x,x);
n>>=1;
}
return res;
}
int Lucas(int a,int b)//return C(n%mod,m%mod)*C(n/mod,m/mod)%mod
{
int res=1;
if(a&&b)
{
int aa=a%mod,bb=b%mod;
if(aa<bb)
return 0;
res=mod_mul(res,(mod_mul(p[aa],mod_pow(mod_mul(p[bb],p[aa-bb]),mod-2)))%mod);//res=res*(p[aa]/(p[bb]*p[aa-bb]))
aa=aa/mod; //此处的除法是乘以逆元
bb=bb/mod;
}
return res;
}
int main()
{
init();
while(cin>>m>>n>>mod)
{
cout<<Lucas(m,n)<<endl;
}
return 0;
}