lucas定理(hdu3037,)

来源:互联网 发布:淘宝收藏 猪八戒网 编辑:程序博客网 时间:2024/05/18 00:46

这几天脑袋秀逗了,想什么都想不出来,上课状态也特别差,老是犯困,看来最近状态不是一般的差,以后不熬夜了,注意作息,调整状态好好学习了。用心看了下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 longconst 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));    }}


0 0
原创粉丝点击