这几天脑袋秀逗了,想什么都想不出来,上课状态也特别差,老是犯困,看来最近状态不是一般的差,以后不熬夜了,注意作息,调整状态好好学习了。用心看了下lucas定理,就整理下来吧。
Lucas定理
定义:
A、B是非负整数,p是质数。AB写成p进制:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。
则组合数C(A,B)与C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0])modp同余
即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)
怎么用:
Lucas定理是用来求 C(n,m) mod p,p为素数的值。(注意:p一定是素数)
有人会想,C(n,m)不能用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式来递推吗?
( 提示:C(n, m) mod p = n!/(m!(n - m)!) mod p )
可以是可以。但当n,m,p都很大时,你递推所用的时间就会很爆炸了。所以,这就需要用到Lucas定理来解决了。
因此,Lucas定理用来解决大组合数求模是很有用的。
心得:当你求解大的组合数,递推特别耗时的时候,并且让你对一个素数取模的时候,那一定就是lucas定理了。
注意:
Lucas定理最大的数据处理能力是p在10^5左右。
表达式:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p。(可以递归)
递归方程:(C(n%p, m%p)*Lucas(n/p, m/p))%p。(递归出口为m==0,return 1)
已知C(n, m) mod p = n!/(m!(n - m)!) mod p。显然是除法取模,这里又要用到m!(n-m)!的逆元。求逆元
根据费马小定理 :
已知(a, p) = 1,则 ap-1 ≡ 1 (mod p), 所以 a*ap-2 ≡ 1 (mod p)。
也就是 (m!(n-m)!)的逆元为 (m!(n-m)!)p-2 。
以上就是卢卡斯定理的内容了
下面是模板代码,模板求得是C(m+n,m),题目出处http://acm.hdu.edu.cn/showproblem.php?pid=3037
#include <iostream>
#include <cstdio>
using namespace std;
#define ll long long
const int mo=1e6+10;
ll fact[mo];
ll n,m,p;
void init()//对阶乘进行预处理
{
fact[0]=1;
for(int i=1;i<=p;i++)
fact[i]=fact[i-1]*i%p;
}
ll quickmod(ll a,ll b)//快速幂
{
ll ans=1;
ll temp=a%p;//不太明白,需要请教
while(b){
if(b&1) ans=ans*temp%p;
temp=temp*temp%p;
b>>=1;
}
return ans;
}
ll comb(ll n,ll m)//comb用来求解组合数
{
if(m>n) return 0;
return fact[n]*quickmod(fact[m]*fact[n-m],p-2)%p;//根据费马小定理求逆元因为a^(p-1)modp==1所以a^(p-2)modp就是a的逆元
}
ll lucas(ll n,ll m)//卢卡斯定理
{
if(m==0) return 1;//出口
else return (comb(n%p,m%p)*lucas(n/p,m/p))%p;//卢卡斯定理
}
int main()
{
ll t;scanf("%lld",&t);while(t--){
scanf("%lld%lld%lld",&n,&m,&p);
init();
printf("%lld\n",lucas(n+m,m));
}
}