zoukankan      html  css  js  c++  java
  • 「算法笔记」组合数学入门

    下面的不用看了,直接看 这里 吧 QAQ(内容更多,更详细,只不过没写二项式定理)

    排列组合

    一些定义

    加法原理:若完成一件事的方法有 n 类,其中第 i 类方法包括 a种不同的方法,且这些方法互不重合,则完成这件事共有 a1+a2+...+an 种不同的方法。

    乘法原理:若完成一件事需要 n 个步骤,其中第 i 个步骤有 a种不同的完成方法,且这些步骤互不干扰,则完成这件事共有 a1×a2×...×an 种不同的方法。

    排列:把 n 个不同元素重新排列,方案数:n!

    从 n 个不同元素中一次取出 m 个元素排成一列,产生的不同排列的数量为:

    组合:从 n 个不同元素中选出 m 个组成一个集合(不考虑顺序),产生的不同集合数量为:

    插板法:x1+x2+...+xn=m 的正整数解的个数为 C(n-1,m-1)。

    一些性质

    C(n,m)=C(n,n-m),规定C(n,0)=1,C(n,n)=1

    证明:对于从n个不同元素中取出m个组成的每个集合,剩余未取的元素也构成一个集合,两个集合一一对应。

    C(n,m)=C(n-1,m)+C(n-1,m-1)

    证明:从n个不同元素中取出m个组成一个集合有两类方法:取n号元素、不取n号元素。若取n号元素,则应在剩余n-1个元素中选出m-1个,有C(n-1,m-1)种取法。若不取n号元素,则应在剩余n-1个元素中选出m个,有C(n-1,m)种取法。根据加法原理得,C(n,m)=C(n-1,m)+C(n-1,m-1)。

    C(n,0)+C(n,1)+C(n,2)+...+C(n,n)=2n

    证明:从n个不同元素中取出若干个元素组成一个集合,有n+1类方法,分别是取出0,1,2,...n个。根据加法原理共有C(n,0)+C(n,1)+C(n,2)+...+C(n,n)种方法。从另一个方面想,n个元素每个元素可以取或不取,总方法数为2n,二者相等。

    组合数的求法

    1.用递推法。C(n,m)=C(n-1,m)+C(n-1,m-1)。复杂度O(n2)。

    c[0][0]=1;
    for(int i=1;i<=n;i++){
        c[i][0]=1;
        for(int j=1;j<=i;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    } 

    2.若题目要求C(n,m)%p的值,并且1~n都存在模p的乘法逆元,则可以先计算分子 n! mod p,再计算分母 m! (n-m)! mod p的逆元,乘起来得到 C(n,m)%p。复杂度为O(n)。

    若在计算阶乘的过程中,把 0≤k≤n 的每个 k! mod p 及其逆元分别保存在两个数组 fac 和 inv中,则可以在 O(n log n) 的预处理后,以 O(1) 的时间回答 0≤y≤x≤n 的所以组合数 C(x,y)%p=fac[x]×inv[y]×inv[x-y]%p。

    //快速幂: 
    int mul(int x,int n,int mod){
        int ans=mod!=1;
        for(x%=mod;n;n>>=1,x=x*x%mod)
            if(n&1) ans=ans*x%mod;
        return ans;
    }
    //对阶乘数及其逆元进行预处理:
    void init(){ 
        f[0]=g[0]=1;
        for(int i=1;i<=n;i++)
            f[i]=f[i-1]*i%mod;    //f(i)表示i! 
        g[n]=mul(f[n],mod-2,mod);    //求逆元 
        for(int i=n-1;i;i--)
            g[i]=g[i+1]*(i+1)%mod;     //g(i)表示 1/(i!) 
    } 
    //之后就可以调用:
    int solve(int n,int m){
        return f[n]*g[m]%mod*g[n-m]%mod;
    } 

    3.若p为质数,可用Lucas定理求解。(Lucas定理和代码在下文会写)

    4.若题目要求对 C(n,m) 进行高精度运算,为避免除法,可把分子、分母快速分解质因数,在数组中保存各项质因子的指数。然后把分子、分母各个质因子的指数对应相减(把分母消去),最后把剩余质因子乘起来,时间复杂度为 O(n log n)。

    二项式定理

    「NOIP2011」计算系数 题目链接

    给定一个多项式 (by+ax)k,请求出多项式展开后 xnym 项的系数,对10007取模。0≤n,m≤k≤1000,n+m=k,0≤a,b≤106

    根据二项式定理,有:

    所以 xny项的系数即为 C(k,n)anbm,直接计算 anbm 和组合数即可。

    #include<bits/stdc++.h>
    #define int long long
    const int N=1010,mod=10007;
    int a,b,k,n,m,c[N][N],ans;
    int mul(int x,int n,int mod){
        int ans=mod!=1;
        for(x%=mod;n;n>>=1,x=x*x%mod)
            if(n&1) ans=ans*x%mod;
        return ans;
    }
    signed main(){
        scanf("%lld%lld%lld%lld%lld",&a,&b,&k,&n,&m),c[0][0]=1;
        for(int i=1;i<=k;i++){
            c[i][0]=1;
            for(int j=1;j<=i;j++)
                c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; 
        }
        ans=mul(a,n,mod)*mul(b,m,mod)*c[k][n]%mod;
        printf("%lld
    ",ans);
        return 0;
    }

    Lucas定理

    若 p 是质数,则对于任意整数 1≤m≤n,有:

      

    也就是把 n 和 m 表示成 p 进制数,对 p 进制下的每一位分别计算组合数,最后再乘起来。

    具体阔以参考这里

    Lucas板子 题目链接

    题目大意:求 C(n,m) mod p 的值。

    1≤m≤n≤109,m≤104,m<p<109,p 是质数。

    直接放个代码叭~ here

    #include<bits/stdc++.h>
    #define int long long
    const int N=1e5+5;
    int t,n,m,p;
    int mul(int x,int n,int mod){
        int ans=mod!=1;
        for(x%=mod;n;n>>=1,x=x*x%mod)
            if(n&1) ans=ans*x%mod;
        return ans;
    }
    int C(int n,int m){
        if(m>n) return 0;
        int x=1,y=1;
        for(int i=n-m+1;i<=n;i++) x=x*i%p;
        for(int i=2;i<=m;i++) y=y*i%p;
        return x*mul(y,p-2,p)%p;
    } 
    int lucas(int n,int m){
        if(!m) return 1;
        return C(n%p,m%p)*lucas(n/p,m/p)%p;
    }
    signed main(){
        scanf("%lld",&t);
        while(t--){
            scanf("%lld%lld%lld",&n,&m,&p);
            printf("%lld
    ",lucas(n,m));
        }
        return 0;
    }
  • 相关阅读:
    封装( 增删改 查 )类
    php注释规范
    php访问mysql数据库
    php 文件限速下载代码
    jQuery鼠标事件汇总
    权限管理
    文件管理 打开-返回上级
    文件操作
    简单的文件上传
    ajax XML
  • 原文地址:https://www.cnblogs.com/maoyiting/p/12620993.html
Copyright © 2011-2022 走看看