• 数论--组合数


    组合数1

    如果只是求一个组合数,当然可以直接用这个公式,用循环来实现,注意不要溢出,可以边乘边除

    但是如果要求求很多个组合数呢??

    一般我们用杨辉三角性质

    杨辉三角上的每一个数字都等于它的左上方和右上方的和(除了边界)

    组合数2

    第n行,第m个就是,就是C(n, m) (从0开始)

    容易实现:

     1 #include<cstdio>
     2 const int N = 2000 + 5;
     3 const int MOD = (int)1e9 + 7;
     4 int comb[N][N];//comb[n][m]就是C(n,m)
     5 void init(){
     6     for(int i = 0; i < N; i ++){
     7         comb[i][0] = comb[i][i] = 1;
     8         for(int j = 1; j < i; j ++){
     9             comb[i][j] = comb[i-1][j] + comb[i-1][j-1];
    10             comb[i][j] %= MOD;
    11         }
    12     }
    13 }
    14 int main(){
    15     init();
    16 }

    时间复杂度为O(n^2)

    因为大部分题都有求余,所以我们大可利用逆元的原理(没求余的题目,其实你也可以把MOD自己开的大一点,这样一样可以用逆元做)(逆元还记得吧,前面提到的!!!)

    我们需要求阶乘和逆元阶乘

     我们就用1e9+7来求余吧

     1 #include<cstdio>
     2 const int N = 200000 + 5;
     3 const int MOD = (int)1e9 + 7;
     4 int F[N], Finv[N], inv[N];    //F是阶乘,Finv是逆元的阶乘 
     5 void init(){
     6     inv[1] = 1;
     7     for(int i = 2; i < N; i ++){
     8         inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;   //求逆元 
     9     }
    10     F[0] = Finv[0] = 1;
    11     for(int i = 1; i < N; i ++){
    12         F[i] = F[i-1] * 1ll * i % MOD;
    13         Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD;
    14     }
    15 }
    16 int comb(int n, int m){    //comb(n, m)就是C(n, m) 
    17     if(m < 0 || m > n) return 0;
    18     return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
    19 }
    20 int main(){
    21     init();
    22     printf("%d
    ",comb(5,2)); 
    23 }

    看一个性质

    C(n,k)=n!/(k!*(n-k)!);

    C(n,k-1)=n!/((k-1)!*(n-k+1)!);

    那么,C(n,k)/C(n,k-1)=(n-k+1)/k

    所以C(n,k)=(n-k+1)/k*C(n,k-1);

    这样,就可以从C(n,0)=1开始从左往右推,得到所有C(n,k)

    当碰到大组合数的时候,就要用到这个性质了!

    卢卡斯说:

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

    对于C(n / p, m / p),如果n / p 还是很大,可以递归下去,一直到世界的尽头

    这就是卢卡斯定理!

    在下卢卡斯...

    上代码:

     1 #include<cstdio>
     2 
     3 typedef long long LL ;
     4 const int N = 200000 + 5;
     5 const int MOD = (int)1e9 + 7;
     6 int F[N], Finv[N], inv[N];    //F是阶乘,Finv是逆元的阶乘 
     7 void init(){
     8     inv[1] = 1;
     9     for(int i = 2; i < N; i ++){
    10         inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;   //求逆元 
    11     }
    12     F[0] = Finv[0] = 1;
    13     for(int i = 1; i < N; i ++){
    14         F[i] = F[i-1] * 1ll * i % MOD;
    15         Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD;
    16     }
    17 }
    18 int comb(int n, int m){    //comb(n, m)就是C(n, m) 
    19     if(m < 0 || m > n) return 0;
    20     return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
    21 }
    22 
    23 LL Lucas(LL n, LL m, int p){
    24     return m ? Lucas(n/p, m/p, p) * comb(n%p, m%p) % p : 1;
    25 }
    26 int main(){
    27     init();
    28     printf("%lld
    ",Lucas(5,2,8)); //C(5,2)%8
    29 }

    Over..

  • 相关阅读:
    SpringBoot整合Apache-CXF实践
    jar包部署指定不同环境
    Cannot create GC thread. Out of system resources.
    更改"xxxx" 的权限: 不允许的操作
    [AWS] Lab: Configure a Cognito user can access DynamoDB for read
    [Tools] VI cmds
    [AWS DA GURU] KMS and Encryption on AWS
    [Linux] Add new sudo user & assign folder owner
    [AWS
    [AWS
  • 原文地址:https://www.cnblogs.com/eastblue/p/7634801.html
Copyright © 2020-2023  润新知