zoukankan      html  css  js  c++  java
  • 浅谈卢卡斯定理

    前几天gryz组织我们听了几天数论,蒟蒻 Nanjo_Qi 自然是听得一点问题也没有。

    于是只能自己yy着学一点其他的数学的东西,正巧在那之前刚刚学会卢卡斯定理,于是现在就来水一篇博客。

    其实是不想做题了。正巧机房装修,吵的一批。

    卢卡斯(Lucas)定理是什么?

    他是用来求组合数 C(n, m) % p 值的定理,这里的p是素数。所以,它是一个解决大组合数求模的算法。所以看起来还是很有用的感觉。

    为了给以后的学习做铺垫,我们先来了解一下乘法逆元:

    逆元是指一个可以取消另一给定元素运算的元素,例如加法中的加法逆元和乘法中的倒数。

    而一个数关于模p意义下的逆元可以利用快速幂,扩展欧几里得算法等求得:

    已知(a, p) = 1,则 ap-1 ≡ 1 (mod p),  所以 a * ap-2 ≡ 1 (mod p) ,也就是 (m!(n-m)!) 的逆元为 (m!(n-m)!)p-2 

     1 typedef u64 long long;
     2 
     3 inline u64 Fast_Pow(u64 k, u64 b) {
     4     u64 ans = 1;
     5     while(b) {
     6         if( b&1 )  ans = ans * k % p;
     7         k = k * k % p, b >>= 1;
     8     }
     9     return ans;
    10 }
    乘法逆元

    然后就可以愉快地学习Lucas了。

    推导过程如下(来自百度百科,计算机竞赛不需要证明,所以不想看就算了(bushi)):

    首先你需要这个算式:

       
     

    其中f > 0 && f < p,然后

    (1 + x) n Ξ (1 + x) sp+q  Ξ ( (1 + x)p)s · (1 + x) q Ξ (1 + xps · (1 + x) q   (mod p)

    所以得(1 + x) sp+q    (mod p)

    我们求左边 (1 + x)sp+q 中的   的系数为:

    求右边公式中的    :

    通过观察你会发现当且仅当i = t , j = r ,能够得到   的系数,即: 

    所以   ,得证。

    反正我是没仔细看懂,所以对不对我也不知道。

    你只要知道:C(n, m) % p = (C(n/p, m/p) % p) * (C(n%p, m%p) % p) % p 就好了吧?(笑

    然后程序可以对 C(n%p, m%p) % p 这个地方递归调用Lucas定理;

    以及前面 C(n, m) % p = n! / ( m!(n - m)! ) % p 的除法取模,求一下 n! / ( m!(n - m)! ) 模p意义下的逆元就好了。

    其实就是一个公式的东西233,很简单的板子,转眼水完了半上午

     1 #include <cstdio>
     2 #include <cctype>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 typedef long long u64;
     8 
     9 const int maxn = 100000 + 10;
    10 u64 n, m, p, fac[maxn];
    11 
    12 inline u64 Fast_Pow(u64 x, u64 k) {
    13     u64 ans = 1;
    14     while(k) {
    15         if( k&1 ) ans = ans * x % p;
    16         k >>= 1, x = x * x % p;
    17     }
    18     return ans;
    19 }
    20 
    21 u64 C(u64 k, u64 b) {
    22     if( k<b )  return 0;
    23     return fac[k] * Fast_Pow(fac[b] % p, p-2) % p * Fast_Pow(fac[k-b] % p, p-2) % p;
    24 }
    25 
    26 u64 Lucas(u64 k, u64 b) {
    27     if( !b )  return 1;
    28     return C(k%p, b%p) * Lucas(k/p, b/p) % p;
    29 }
    30 
    31 int main(int argc, char const *argv[])
    32 {
    33     int t;  scanf("%d", &t);
    34     while( t-- ) {
    35         fac[0] = 1;
    36         scanf("%lld%lld%lld", &n, &m, &p);
    37         for(int i=1; i<=p; ++i)
    38             fac[i] = fac[i-1] * i % p;
    39         printf("%lld
    ", Lucas(n+m, m));
    40     }
    41     return 0;
    42 }

                   ——「看来『那个』不是什么温柔的谎言。」
                   ——「似乎是这样。」雾子微微笑着说,「最后能知道这件事实在太好了。」

  • 相关阅读:
    并发容器梳理
    CAS总结
    原子类总结and-Git提交出现error: src refspec master does not match any的问题
    简单工厂模式小结
    JVM学习与问题总结——java内存区域与内存溢出异常
    反射机制学习记录
    观察者模式
    IDEA的一些常用设置
    建造者模式
    【[AH2017/HNOI2017]礼物】
  • 原文地址:https://www.cnblogs.com/nanjoqin/p/9091770.html
Copyright © 2011-2022 走看看