题目大意:共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;
}
}
本文介绍了一种利用组合数学解决特定计数问题的方法,并详细解释了如何通过快速幂、组合数计算、Lucas定理及乘法逆元等技术求解。针对大规模数据集,文章提供了高效的C++实现代码。
188

被折叠的 条评论
为什么被折叠?



