组合数C,n,m的四种求解方法

转自:文章

1、暴力求解

C(n,m)=n*(n-1)*...*(n-m+1)/m!,(n<=15);

int CF(int n,int m)
{
    int ans=1,i,j;
    for(i=n;i>=n-m+1;i--)
    ans*=i;
    for(i=m;i>=2;i--) 
    ans/=i;
    return ans;
}

2、打表

C(n,m)=C(n-1,m-1)+C(n-1,m),(n<=10000);

const int maxn = 10010;
const int MOD = 100007;
void CF(int n,int m)
{
    int i,j;
    for(i=0;i<=maxn;i++)
    {
        c[0][i]=0;c[i][0]=1;
    }
    for(i=1;i<=maxn;i++)
        for(j=1;j<=maxn;j++)
        c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
}

3、质因数分解

C(n,m)=n!/(m!*(n-m)!),C(n,m)=p1a1-b1-c1p2a2-b2-c2…pkak-bk-ck,(n<=10000000)

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int MOD = 100007;
const int maxn = 1000001;
bool a[maxn]={false};
vector <int> prim_produce() //生成素数序列 
{
    vector <int> vc;
    vc.push_back(2);
    int i,j;
    for(i=3;i*i<=maxn;i+=2)
    {
        if(!a[i])
        {
            vc.push_back(i);
            for(j=i*i;j<=maxn;j+=i)
            {
                a[j]=true;
            }
        }
    }
    while(i<maxn)
    {
        if(!a[i]) vc.push_back(i);
        i+=2;
    }
    return vc;
}
//计算n!素数p的指数 
int cal(int x,int p)
{
    int ans=0;
    long long re=p;
    while(x>=re)
    {
        ans+=x/re;
        re*=p;
    }
    return ans; 
}

int Pow(long long n,int k) //二分求n的k次方 
{
    long long ans=1;
    while(k)
    {
        if(k&1) ans=ans*n%MOD;
        n=(n*n)%MOD;
        k>>=1;
    }
    return ans;
}
int comb(int n,int m) //计算公式 
{
    vector <int> prim=prim_produce();
    long long ans=1;
    int num;
    for(int i=0;i<prim.size()&&prim[i]<=n;i++)
    {
        num=cal(n,prim[i])-cal(m,prim[i])-cal(n-m,prim[i]);
        ans=(ans*Pow(prim[i],num))%MOD;
    }
    return ans;
}
int main(void)
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        printf("%d\n",comb(n,m));
    }
    return 0;
}
View Code