3
题意:
求把m个果实放到n个树的方案数,并将答案对p求余。
题目链接:Saving Beans
解题思路:
将题目转化为方程x1+x2+x3+...+xn = m 的解的数目,即n个树上的果子数之和等于m,利用插板法思想,就是把m分
解成n个数之和,把m看成m个1,那么有m+1个空,以后每插入一个板子后,空都比之前多一个,m分成n个数之和只要
插n-1个板子,然后板子先后插是没有差别的,根据组合数知识,要除个(n-1)!,
即(m+1)*(m+2)*...(m+n-1) = C(n+m-1,n-1)= C(n+m-1, m)
然后考虑从0~m所有果子个数的情况,利用组合数知识,C(n, k) + C(n,k+1) = C(n+1, k+1);
有 C(n-1,0)+C(n,1)+...C(n+m-1, m) = C(n, 0) +C(n,1)+...C(n+m-1, m) = C(n+m,m).
那么答案就是求C(n+m,m) % p 的值。
而Lucas定理就是专门解决类似于此的大组合数求余算法。
定理内容感觉还需要时间看看,先保存下模板
代码:
#include
#include
using namespace std;
typedef long long LL;
LL mod;
LL fac[100010];
LL pow_mod(LL x, LL n) //快速幂取模
{
LL sum = 1;
while(n) {
if(n & 1) sum = sum * x % mod;
x = x * x % mod;
n >>= 1;
}
return sum;
}
void init() //初始化所需要的阶乘
{
fac[0] = 1;
for(int i = 1;i <= mod;i++)
fac[i] =fac[i-1] * i % mod;
}
LL Lucas(LL n, LL m)//Lucas定理,计算C(n,m)%mod
{
LL ans = 1;
while(n && m) {
LL a = n % mod, b = m % mod;
if(a < b) return 0;
ans = ans * fac[a] * pow_mod(fac[b]*fac[a-b] % mod, mod-2) % mod;
n /= mod;
m /= mod;
}
return ans;
}
int main()
{
int T;
LL n, m;
scanf("%d",&T);
while(T--) {
scanf("%I64d%I64d%I64d",&n, &m, &mod);
init();
printf("%I64d\n", Lucas(n+m, m));
}
return 0;
}