zoukankan      html  css  js  c++  java
  • 数论组合数

    组合数的求法

    1.打表法

    //组合数打表模板,适用于N<=3000
    //c[i][j]表示从i个中选j个的选法。
    long long C[N][N];
    
    void get_C(int maxn)
    {
        C[0][0] = 1;
        for(int i=1;i<=maxn;i++)
        {
            C[i][0] = 1;
            for(int j=1;j<=i;j++)
                C[i][j] = C[i-1][j]+C[i-1][j-1];
            //C[i][j] = (C[i-1][j]+C[i-1][j-1])%MOD;
        }
    }

    2.逆元法

    C(n,k)==A(n,n) / ( A(k,k)*A(n-k,n-k))

    void inint(){
        j[1]=1;
        for(ll i=2;i<maxn;i++){
            j[i]=i*j[i-1]%M;//阶乘打表 
        }
    } 
    ll C(ll n,ll m){
        ll ans1=kuai(j[m],M-2)%M*kuai(j[n-m],M-2)%M;
        return j[n]*ans1%M;
    } 

    3卢卡斯定理

    1≤n,m,p≤10^5,1≤T≤10,p为素数

    ll qpow(ll a,ll b){
        ll ans=1;
        while(b){
            if(b&1){
                ans=(ans*a)%p;
            }
            a=(a*a)%p;
            b>>=1;
        } 
        return ans;
    }
    ll C(ll n,ll m){
        if(n<m) return 0;
        if(m>n-m){
            m=n-m;
        }
        ll a=1,b=1;
        for(int i=0;i<m;i++){
            a=(a*(n-i))%p;
            b=(b*(i+1))%p;
        }
        return a*qpow(b,p-2)%p;
    }
    ll Lucas(ll n,ll m){
        if(m==0){
            return 1;
        } 
        else{
            return Lucas(n/p,m/p)*C(n%p,m%p)%p;
        }
    }

    组合数求和(N一定)

    例:

    据说一间6人宿舍有7个聊天群^_^。但是这个数字可以更大:因为每三个或更多的人可以组成一个聊天组,所以可以有42个不同的聊天组。

    假设一个宿舍里有N个人,每K个或更多的人可以组成一个聊天组,有多少个不同的聊天组?

    解释:C(6,3)+C(6,4)+C(6,5)+C(6,6)

    输入从一行开始,其中包含一个整数T,这是测试用例的数量。

    每个测试用例包含一行,其中有两个整数N和K,表示一个宿舍的人数和组成一个聊天组的最小人数。

    1≤T≤100。

    1≤N≤1e9。

    3≤K≤1e5。

    输出

    对于每个测试用例,输出一行包含“案例#x: y”的代码,其中x是测试用例号(从1开始),y是不同聊天组的数量,模块为1000000007。

    解析:因为N的范围是1e9以内,所以不能用逆元的方法

    #pragma GCC optimize(2)
    #include <iostream>
    #include <string>
    #include <cstdio>
    #include <stack>
    #include <queue>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    typedef long long ll;
    ll read(){
        ll x=0; ll f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    using namespace std;
    const int maxn=1e6+100;
    const ll mod=1e9+7;
    const int N=5e5+10;
    //C(n,k)+C(n,k+1)+C(n,k+2)+-----C(n,n)
    //C(n,1)+C(n,2)+C(n,3)-----C(n,n)-C(n,1)-C(n,2)-C(n,3)----C(n,k-1)
    //c(n,k)==A(n,n)/(A(k,k)*A(n-k,n-k)
    ll n,k;
    ll qpow(ll a,ll b){
        ll ans=1;
        while(b){
            if(b&1){
                ans=(ans*a)%mod;
            }
            a=(a*a)%mod;
            b>>=1;
        }
        return ans%mod;
    }
    ll inv(ll a){
        return qpow(a,mod-2)%mod;
    } 
    int main(){
        int t;
        cin>>t;
        int Case=1;
        while(t--){
            scanf("%lld%lld",&n,&k); 
            if(k>n){
                printf("Case #%d: 0
    ",Case++);
                continue;
            }
            ll ans=qpow(2,n);
            ll sum=1+n;//c(n,0)+c(n,1)
            ll temp=n;//C(n,1)
            //sum=1+n
            
            //c(n,i+1)=c(n,i)*(n-i)-/(i+1)
            for(ll i=1;i<=k-2;i++){
                temp=((temp*(n-i)%mod)*inv(i+1))%mod;
                sum=(sum+temp)%mod;
            } 
            printf("Case #%d: %lld
    ",Case++,(ans-sum+mod)%mod);
        } 
    } 

     


    Cmn=C(m/p)(n/p)C(m%p)(n%p)(mod p)Cnm=C(n/p)(m/p)∗C(n%p)(m%p)(mod p)

    观察上述表达式,可知 n%pn%p和 m%pm%p 一定是小于 pp 的数,可以直接求解。
    C(m/p)(n/p)C(n/p)(m/p) 可以继续用 LucasLucas 定理求解,这也就要求 pp 的范围不能够太大,一般在 1e51e5 左右。
    边界条件:当 m=0m=0 的时候,返回 11 。
    复杂度:O(logn+p)

  • 相关阅读:
    ASP.NET2.0中将文件上传到数据库
    C#中数据类型转换
    数据绑定以及Container.DataItem的具体分析
    CodeSmith开发系列资料总结
    Jquery信息专题收集
    microsoft .net framework专题汇总
    ASP.NET中插入FLASH代码
    javascript专题汇总
    经典SQL行列转换
    前台js调用后台c#方法
  • 原文地址:https://www.cnblogs.com/lipu123/p/13842186.html
Copyright © 2011-2022 走看看