HDU3037 Saving Beans

本文介绍了一种利用组合数学解决特定计数问题的方法,并详细解释了如何通过快速幂、组合数计算、Lucas定理及乘法逆元等技术求解。针对大规模数据集,文章提供了高效的C++实现代码。
  • 题目大意:共T个测试点,每个测试点中,给定n、m,求将不超过m个种子放入n个坑的方案总数,最后答案对质数p取模。

  • 数据范围:1 <= n, m <= 1000000000, 1 < p < 100000。

  • 思路:原题意即求方程x1+…+xn=m解的个数,因为中间的每一项均可为0,故两面同时将加上n,转换成为x1+…+xn+n=m+n,即x1’+…+xn’=m+n,其中每一项至少为1,则由隔板法可知,解的个数为C(n+m-1,m),这只是恰好为m个的方案数,总数为ans=C(n-1,0)+C(n,1)+…+C(n+m-1,m)=C(n+m,m)。

  • 知识储备:

    • (A * B) mod C = (A mod C) * (B mod C) mod C
    • Lucas定理:记Lucas(n,m,p)=C(n,m) mod p,则Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p)。
    • 乘法逆元:假如p是质数,且a、p互质,那么a的(p-1)次方除以p的余数恒为1,那么a和a^(p-2)互为乘法逆元,则(b / a) = (b * a^(p-2) ) mod p。
  • 注意:中途必须强制转换类型,否则出错。

  • 代码如下:

#include<iostream>
using namespace std;
typedef long long LL;
LL t,n,m,p;

LL power(LL n,LL m)                                         /*快速幂*/ 
{
    LL ans=1;
    while (m!=0)
    {
        if (m/2==1)
        {
            ans=( (LL)ans * (LL)n )%p;
        }
        m/=2;
        n=( (LL)n * (LL)n )%p;      
    }   
    return ans;
}

LL C(LL n,LL m)                                                 /*组合数*/ 
{
    LL a=1,b=1;
    if (m>n) return 0;
    while (m)
    {
        a=(a*n)%p;
        b=(b*m)%p;
        /*最后a=n*(n-1)...*(n-m+1),b=m!*/ 
        --m;                                                    
        --n;        
    }
    return ( (LL)a * (LL)power(b,p-2) )%p;  
    /*应用了乘法逆元*/
}

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

int main()
{
    cin>>t;
    for (int i=1;i<=t;++i)
    {
        cin>>n>>m>>p;
        cout<<lucas(n+m,m,p)<<endl;             
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值