zoukankan      html  css  js  c++  java
  • 组合数的几种计算方法

    组合数一种是OI中比较常用的知识

    除了实际的分析之外,我们要考虑的,就是如何快速计算组合数

    下面介绍几种常用的计算组合数的方法


    朴素公式法

    顾名思义,直接套公式
    int C(int n,int m){
    	int ans=1;
    	for(int i=1;i<=m;i++) ans=ans*(n-m+i)/i;
    	return ans;
    }

    如果要对质数P取模,就是这样:
    int C(int n,int m){
    	int ans=1;
    	for(int i=1;i<=m;i++) ans=(LL)ans*(n-m+i)*inverse(i)%P;
    	return ans;
    }
    其中inverse是i对P的逆元,如果P是质数inverse(i)=i^(P-2)   【费马小定理得】,否则用exgcd求出【欧拉定理也行】


    递推式法

    C(n,m)=C(n-1,m-1)+C(n-1,m)
    边界:C(i,0)=C(i,i)=1
    这个递推式用到了动态规划的思想,对于第m个物品,有取和不取两种情况
    可以在O(n^2)的时间内算出所有的C(n,m)

    void cal(){
    	C[0][0]=1;
    	for(int i=1;i<=n;i++){
    		C[i][0]=C[i][i]=1;
    		for(int j=1;j<=(i>>1);j++){
    			C[i][j]=C[i][i-j]=(C[i-1][j-1]+C[i-1][j])%P;
    		}
    	}
    }


    Lucas定理


    对于n和m比较大(<=10^18)的组合数,就要用到LUCAS定理啦


    Lucas定理解决的是n,m比较大而p是小于100000质数


    简而言之就是Lucas(n,m)=C(n%p,m%p)*Lucas(n/p,m/p)%p;

    其中组合数C是用任意一种计算10五次方内取模的组合数计算

    比如可以预处理阶乘fac[i],然后直接C(n,m)=fac[n]*quickpow(fac[n-m]*fac[m],p-2)%p;

    或者O(n)套公式直接算也可以

    要注意n可能小于m,因为是取模后的结果,这个时候返回0【不然会RE】

     

    LL Lucas(LL n,LL m){  
        if(n<m||!m) return 1;  
        return C(n%P,m%P)*Lucas(n/P,m/P)%P;  
    } 


    配合Lucas定理,由于n和m都在10^5范围内,使用预处理阶乘的方法再好不过了:


    预处理阶乘法


    其实就是直接套公式,只不过使用了 预处理阶乘+逆元,查询复杂度O(log100000),非常低

    void cal(){  
        fac[0]=1;  
        for(int i=1;i<=P;i++) fac[i]=fac[i-1]*i%P;  
    }  
      
    inline LL qpow(LL a,LL b){  
        LL ans=1;  
        for(;b;b>>=1,a=a*a%P) if(b&1) ans=ans*a%P;
        return ans;  
    }  
      
    inline LL C(LL n,LL m){  
        if(n<m) return 0;  
        return fac[n]*qpow(fac[n-m]*fac[m]%P,P-2)%P;  
    }  




  • 相关阅读:
    iftop 安装流程
    Centos 6.5 Tengine 安装流程
    linux 查看系统进程前十
    Centos 6.5 mongodb 安装流程
    linux 磁盘查看方式
    Linux 磁盘分区及挂载
    linux 路由添加
    rsyslog 重启
    文件上传到Web服务器
    一些链接1
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282883.html
Copyright © 2011-2022 走看看