[HDU 3037] Saving Beans (隔板法+lucas定理)

HDU - 3037

有不超过 m个球,放入 n个盒子里,求方案数


m个球放入 n个盒子,允许盒子为空
用隔板法可得方案数为 C(m+n-1, m)
所以即求 C(n-1, 0) + C(n, 1) + C(n+1, 2) + … + C(m+n-1, m)
利用组合数公式 C(n, k) = C(n-1, k-1) + C(n-1, k)
原式 = C(n, 0) + C(n, 1) + C(n+1, 2) + … + C(m+n-1, m)
= C(n+1, 1) + C(n+1, 2) + … + C(m+n-1, m)
= C(m+n, m)

由于 m和 n都很大,不可能处理那么多阶乘,
所以利用 lucas定理,这样一来只需要处理到模数 p的阶乘即可
C(n,k)%p = C(n%p,k%p)*C(n/p,k/p)
问题就解决了

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))

const int maxp=1e5+10;
LL N,M,P;
LL fact[maxp];

LL Pow(LL,LL,LL);
LL Lucas(LL,LL,LL);

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    int T;
    scanf("%d", &T);
    for(int ck=1; ck<=T; ck++)
    {
        scanf("%lld%lld%lld", &N, &M, &P);
        fact[0]=1;
        for(int i=1; i<=P; i++) fact[i]=(fact[i-1]*i)%P;
        cout << Lucas(N+M,M,P) << '\n';
    }
    return 0;
}

LL Pow(LL x, LL n, LL p)
{
    LL res=1;
    while(n)
    {
        if(n&1) res=(x*res)%p;
        x=(x*x)%p;
        n>>=1;
    }
    return res;
}

LL Lucas(LL n, LL k, LL p)
{
    LL res=1;
    while(n&&k)
    {
        LL n0=n%p, k0=k%p;
        if(n0<k0) return 0;
        res = res*fact[n0]*Pow(fact[k0]*fact[n0-k0]%p, p-2, p)%p;
        n/=p;
        k/=p;
    }
    return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值