• Lucas定理(卢卡斯定理)


    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(int p) 
     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,int mod)
     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     return res;
    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     return res;
    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<=10,很明显,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 namespace std;
     4 typedef long long ll;
     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     return ans;
    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     return ans;
    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 int main()
    37 {
    38     int t;
    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 }
  • 相关阅读:
    树莓派frp添加为服务管理
    liunx开源打印驱动foo2zjs编译小坑
    树莓派中实现ll命令
    Windows中使用QEMU创建树莓派虚拟机
    C#打印条码BarTender SDK打印之路和离开之路(web平凡之路)(转)
    数据库连接池问题 Max Pool Size
    C#时间
    XAF 如何从Excel复制多个单元格内容到GridView(收藏)
    C#日期处理(转) 太忘记了,备忘
    C#、devExpress 的 给bandedGrid加菜单功能 :复制、粘贴的例子(转)
  • 原文地址:https://www.cnblogs.com/theshorekind/p/12795947.html
Copyright © 2020-2023  润新知