• Lucas定理及其应用


    Lucas定理这里有详细的证明。

    其实就是针对n, m很大时,要求组合数C(n, m) % p, 一般来说如果p <= 10^5,那么就能很方便的将n,m转化为10^5以下这样就可以按照乘法逆元的方法求解。

    定义:

    C(n, m) = C(n%p, m%p)*C(n/p, m/p) (mod p)

    一种比较好理解的证明方式是这样的, 上面资料中有提到,

    由p为质数,(1+x)^p = 1+x^p (mod p) p为质数,然后就是下面这幅图的内容了。

    将n, m分别表示成p进制,n = n/p*p+a0, m = m/p*p+b0;

    那么对于上面式子x^m的系数,左右两部分肯定是相等的,左边系数C(n, m) ,  而m = m/p*p+b0, 那么i和j分别对应m/p, 和bo

    所以就可以得到证明:C(n, m) = C(n%p, m%p)*C(n/p, m/p) (mod p)。

    下面就是具体题目了:

    HDU 3037 Saving Beans

    http://acm.hdu.edu.cn/showproblem.php?pid=3037

     1 #include <iostream>
     2 #include <string.h>
     3 #include <stdio.h>
     4 using namespace std;
     5 
     6 #define N 100010
     7 
     8 long long mod_pow(int a,int n,int p)
     9 {
    10     long long ret=1;
    11     long long A=a;
    12     while(n)
    13     {
    14         if (n & 1)
    15             ret=(ret*A)%p;
    16         A=(A*A)%p;
    17         n>>=1;
    18     }
    19     return ret;
    20 }
    21 
    22 long long factorial[N];
    23 
    24 void init(long long p)
    25 {
    26     factorial[0] = 1;
    27     for(int i = 1;i <= p;i++)
    28         factorial[i] = factorial[i-1]*i%p;
    29     //for(int i = 0;i < p;i++)
    30         //ni[i] = mod_pow(factorial[i],p-2,p);
    31 }
    32 
    33 long long Lucas(long long a,long long k,long long p) //求C(n,m)%p p最大为10^5。a,b可以很大!
    34 {
    35     long long re = 1;
    36     while(a && k)
    37     {
    38         long long aa = a%p;long long bb = k%p;
    39         if(aa < bb) return 0; //这个是最后的改动!
    40         re = re*factorial[aa]*mod_pow(factorial[bb]*factorial[aa-bb]%p,p-2,p)%p;//这儿的求逆不可先处理
    41         a /= p;
    42         k /= p;
    43     }
    44     return re;
    45 }
    46 
    47 int main()
    48 {
    49     int t;
    50     cin >> t;
    51     while(t--)
    52     {
    53         long long n,m,p;
    54         cin >> n >> m >> p;
    55         init(p);
    56         cout << Lucas(n+m,m,p) << "
    ";
    57     }
    58     return 0;
    59 }
    View Code

     分析:

    题意容易转化为x1+x2+...xn <= m的解,最后结果就是C(n+m, m),但为什么呢?

    我们可以这样分析: 

    每棵树上能够放的豆子数目i,然后用一个式子x^i表示,然后每棵树有0, 1, 2, m种可能的放置数目,用一个多项式(1+x^1+x^2+.....x^m)表示,等比数列求和(1-x^(m+1)/(1-x),n棵树就是n次方。

    即问题变成了,[1-x^(m+1)]^n*1/(1-x)^n的i<=m 的各个x^i的系数之和, 对于1/(1-x)^n, 借用无穷级数的直接结论 = ∑C(n+k-1, n-1)x^k , k >= 0,很显然前面部分[1-x^(m+1)]^n只能为最小幂次也就是x^0,后面就是x^i,问题变成了,C(n+k-1, n-1) 0 <= k <= m,化简一下就是C(n+m, n).

    具体求解的时候,C(n, m) = C(n%p, m%p) * C(n/p, m/p) mod p ,一旦C(x, y) x < y 那么返回0, 而且求解的时候是直接乘以阶乘的逆元。

    HDU 4349  Xiao Ming's Hope

     http://acm.hdu.edu.cn/showproblem.php?pid=4349

     1 #include <cstdio>
     2 #include <iostream>
     3 #define bug(x) printf("%d
    ", (x));
     4 #define in freopen("solve_in.txt", "r", stdin);
     5 
     6 using namespace std;
     7 typedef long long LL;
     8 int main(){
     9     
    10     LL n;
    11     while(scanf("%I64d", &n) == 1){
    12         int x = __builtin_popcount(n);
    13         LL ans = 1, a = 2;
    14         while(x){
    15             if(x&1) ans = ans*a;
    16             a = a*a;
    17             x >>= 1;
    18         }
    19         printf("%I64d
    ", ans);
    20     }
    21     return  0;
    22 }
    View Code

     分析:

    题目要求C(n, i) i <= n结果中奇数个数,很明显就是sum{C(n, i)%2| i <= n}, 模2的结果是0, 1, 最终结果只有C(1, 0) , C(1, 1) , C(0, 0)为1, C(0, 1)为0,n和i的二进制表示akak-1...a2a1a0和

    bkbk-1...b2b1b0中,对应位置不能出现ai = 0, bi = 1,结果为0, 其余情况结果都会为1,那么问题就转为x = n中二进制位为1个数,答案是2^x。

    451E - Devu and Flowers

    http://codeforces.com/problemset/problem/451/E

     1 #include <cstdio>
     2 #include <iostream>
     3 #define bug(x) printf("%d
    ", (x));
     4 #define in freopen("solve_in.txt", "r", stdin);
     5 using namespace std;
     6 typedef long long LL;
     7 
     8 const int maxn = 22;
     9 const int M = (int)1e9+7;
    10 
    11 LL inv[maxn], fac[maxn];
    12 LL powmod(LL a, LL b, LL c) {
    13     LL res = 1;
    14     while(b) {
    15         if(b&1) res = res*a%c;
    16         a = a*a%c;
    17         b >>= 1;
    18     }
    19     return res;
    20 }
    21 void pre() {
    22     fac[0] = 1;
    23     for(int i = 1; i < maxn; i++) fac[i] = fac[i-1]*i%M;
    24     for(int i = 1; i < maxn; i++) inv[i] = powmod(fac[i], M-2, M);
    25 }
    26 LL nCr(LL n, LL m) {
    27     n %= M;
    28     m %= M;
    29     if(m == 0) return 1;
    30     if(n < m) return 0;
    31     LL res = 1;
    32     for(int i = 0; i < m; i++)
    33         res = res*(n-i)%M;
    34     res = res*inv[m]%M;
    35     return res;
    36 }
    37 LL f[maxn];
    38 void solve(LL n, LL ss) {
    39     LL ans = 0;
    40     for(int s = 0; s < (1<<n); s++) {
    41         LL sum = 0;
    42         int tag = 1;
    43         for(int i = 0; i < n; i++) if(s&(1<<i)) {
    44                 sum += f[i];
    45                 if(sum > ss) break;
    46                 tag *= -1;
    47             }
    48 
    49         if(sum <= ss){
    50             ans = (ans+nCr(n+ss-sum-1, n-1)*tag+M)%M;
    51         }
    52     }
    53     if(ans < 0) ans += M;
    54     printf("%I64d
    ", ans);
    55 }
    56 
    57 int main() {
    58 
    59     pre();
    60     LL n, s;
    61     scanf("%I64d%I64d", &n, &s);
    62     for(int i = 0; i < n; i++) scanf("%I64d", f+i), f[i]++;
    63     solve(n, s);
    64     return 0;
    65 }
    View Code

    n个盒子每个盒子fi朵鲜花,选出s朵花,共有多少种选法?

     分析:

    每个盒子用(1+x^1+x^2..........x^fi)表示,总表达式∏(1+x^1+x^2..........x^fi) (0 <= i < n) = ∏(1-x^(fi+1)(0 <= i < n) * 1/(1-x)^n, 答案就是x^s的系数。

    同样分成两部分 ∏(1-x^(fi+1)(0 <= i < n)和 1/(1-x)^n,后一部分很好求, x^k系数就是 C(n+k-1, n-1),前一部分x^i通过二进制表示法求出, 二进制每个位为1则表示拿出-x^(fi+1)相乘。那么问题关键在于C(n+k-1, n-1),对于C(n, m) = C(n%p, m%p)*C(n/p, m/p) mod p 由于m很小C(n/p, m/p)为1,那么C(n%p, m%p)很容易求得。

  • 相关阅读:
    Android进阶篇判断3G/WIFI/WAP
    Android基础篇配置查看Android系统源码
    Android进阶篇Http协议
    Android进阶篇流量统计
    Android进阶篇自定义Menu(设置Menu的背景及文字属性)
    Android进阶篇Gson解析Json数据
    Android进阶篇PopupWindow的使用
    Android数据篇SAX解析XML
    Android进价篇重力感应
    Android进阶篇ListView和Button共存
  • 原文地址:https://www.cnblogs.com/rootial/p/3998868.html
Copyright © 2020-2023  润新知