Lucas定理(卢卡斯定理)

摘要:
Lucas定理Lucas定理适用于组合数C(n,m)modp时,n和m较大,但是p为素数的时候。其直接应用为:C(n,m)%p=C*C%p,即Lucas(n,m)%p=Lucas*C%p求上式的时候,Lucas递归出口为m=0时返回1求C%p的时候,此处写成C(n,m)%pC(n,m)%p=n!给出有费马小定理与Lucas定理求解组合数中n,m较大且mod一个数的代码:1llf[N];//f[n]=n!上面递归也说了,我们适应于本题,Lucas%p=Lucas*C%p,。Lucas是递归式,当m==0时,Lucas=1,为递归出口。代码如下:1#include2#include3usingnamespacestd;4typedeflonglongll;5llpow_mod//快速幂6{7llans=1;8while9{10if(y&1)11ans=ans*x%mod;12x=x*x%mod;13y˃˃=1;14}15returnans;16}17llC//求递归式中C%p18{19if(m˃n)20return0;21llans=1,a,b;22for//循环求阶乘23{24a=%p;25b=i%p;26ans=ans*%p;//费马小定理求逆元27}28returnans;29}30lllucas//Lucas定理的递归31{32if33return1;34returnC*lucas%p;35}36intmain()37{38intt;39lln,m,p;40cin˃˃t;41while(t--)42{43scanf;44llans;45ans=lucas;46printf;47}48return0;49}

Lucas定理

Lucas定理适用于组合数C(n,m)mod p 时,n和m较大,但是p为素数的时候。

原理就不写了。

其直接应用为:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p,即Lucas(n,m)%p=Lucas(n/p,m/p)*C(n%p,m%p)%p

求上式的时候,Lucas递归出口为m=0时返回1

求C(n%p, m%p)%p的时候,此处写成C(n, m)%p(p是素数,n和m均小于p)

C(n, m)%p = n! / (m ! * (n - m )!) % p = n! * mod_inverse[m! * (n - m)!, p] % p(mod_inverse是指 ...的逆元)

由于p是素数,有费马小定理可知,m! * (n - m)! 关于p的逆元就是m! * (n - m)!的p-2次方。

(求逆元详见乘法逆元)

给出有费马小定理与Lucas定理求解组合数中n,m较大且mod一个数的代码:

(n和m较小的时候可用打表的方法)

1 ll f[N];//f[n] = n!
2 void init(intp) 
3 {       
4     f[0]=1;
5     for(int i=1;i<=p;i++)
6     f[i]=f[i-1]*i%p;
7 } 
8 ll pow_mod(ll x,ll y,intmod)
9 {
10     ll res=1;
11     while(y)
12 {
13         if(y&1)
14         res=res*x%mod;
15         x=x*x%mod;
16         y>>=1;
17 }
18     returnres;
19 }
20 ll Lucas(ll n,ll k,int p)//C (n, k) % p
21 {
22     ll res=1;
23     while (n&&k) 
24 {
25         ll nn=n%p,kk=k%p;
26         if(nn<kk)
27         return 0; 
28         res=res*f[nn]*pow_mod(f[kk]*f[nn-kk]%p,p-2,p)%p;//inv (f[kk]) = f[kk] ^ (p - 2) % p
29         n/=p;
30         k/=p;
31 }
32     returnres;
33 }
34 int main(void)
35 {
36 init(p);
37     printf("%lld
",Lucas(n,m,p));
38     return 0;
39 }

看一个例题:#10228. 「一本通 6.6 例 3」组合 原题链接:https://loj.ac/problem/10228

具体题目不写了,这个题 1<=n,m<=109 ,m<=p<=109,很明显,n,m,p都太大了,打表会溢出,但是可以用递归来实现。

上面递归也说了,我们适应于本题,Lucas(n,m,p)%p=Lucas(n/p,m/p,p,p)*C(n%p,m%p,p)%p,(n,m是组合数,p是取模数,对应传值)。

Lucas(n/p,m/p,p,p)是递归式,当m==0时,Lucas(x,0,p)=1,为递归出口。

而C(n%p,m%p,p)%p可以应用费马小定理和乘法逆元求解。带上快速幂。

代码如下:

1 #include<iostream>
2 #include<cstdio>
3 using namespacestd;
4 typedef long longll;
5 ll pow_mod(ll x,ll y,ll mod)//快速幂 
6 {
7     ll ans=1;
8     while(y)
9 {
10         if(y&1)
11         ans=ans*x%mod;
12         x=x*x%mod;
13         y>>=1;
14 }
15     returnans;
16 }
17 ll C(ll n,ll m,ll p)//求递归式中C(n%p,m%p)%p 
18 {
19     if(m>n)
20     return 0;
21     ll ans=1,a,b;
22     for(ll i=1;i<=m;i++)//循环求阶乘 
23 {
24         a=(n+i-m)%p;
25         b=i%p;
26         ans=ans*(a*pow_mod(b,p-2,p)%p)%p;//费马小定理求逆元 
27 }
28     returnans;
29 }
30 ll lucas(ll n,ll m,ll p)//Lucas定理的递归 
31 {
32     if(m==0)
33     return 1;
34     return C(n%p,m%p,p)*lucas(n/p,m/p,p)%p;
35 }
36 intmain()
37 {
38     intt;
39 ll n,m,p;
40     cin>>t;
41     while(t--)
42 {
43         scanf("%lld%lld%lld",&n,&m,&p);
44 ll ans;
45         ans=lucas(n,m,p);
46         printf("%lld
",ans);
47 }
48     return 0;
49 }

免责声明:文章转载自《Lucas定理(卢卡斯定理)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇入门系列- ABP 本地化Git -- 撤销修改下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章