【转】组合数取模

组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。

组合数取模就是求的值,当然根据的取值范围不同,采取的方法也不一样。

接下来,我们来学习一些常见的取值情况

(1)

     这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于的范围小,直接两层循环即可。

(2),并且是素数

     这个问题有个叫做Lucas的定理,定理描述是,如果

     那么得到

     这样然后分别求,采用逆元计算即可。

题目:http://acm.fzu.edu.cn/problem.php?pid=2020

题意:,其中,并且是素数。

代码:

#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
typedef long long LL;

LL n,m,p;

LL quick_mod(LL a, LL b)
{
    LL ans = 1;
    a %= p;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % p;
            b--;
        }
        b >>= 1;
        a = a * a % p;
    }
    return ans;
}

LL C(LL n, LL m)
{
    if(m > n) return 0;
    LL ans = 1;
    for(int i=1; i<=m; i++)
    {
        LL a = (n + i - m) % p;
        LL b = i % p;
        ans = ans * (a * quick_mod(b, p-2) % p) % p;
    }
    return ans;
}

LL Lucas(LL n, LL m)
{
    if(m == 0) return 1;
    return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%I64d%I64d%I64d", &n, &m, &p);
        printf("%I64d
", Lucas(n,m));
    }
    return 0;
}

这是杨辉三角求组合数的代码:

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

由杨辉三角推出

void init(long long n,long long m)
{
    long long i,j;
    memset(c,0,sizeof(c));
    for(i=0;i<=m;i++)
     c[0][i]=c[1][i]=1;
    for(i=0;i<=m;i++)
     c[i][i]=1;
    for(i=0;i<=n;i++)
     c[i][0]=1;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=m;j++)
        {
            if(i!=j)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
    }
}
原文地址:https://www.cnblogs.com/liuzhanshan/p/6293131.html