「题解」「HDU3037」Saving Beans

题目

不超过 \(n\) 个球放入 \(m\) 个盒子方案数,盒子可以为空,求方案数。

题解

网上有很多使用公式推导的方法,这里介绍一种纯隔板法分析的思路。


首先分析一个子问题:将 \(x\) 个球放进 \(y\) 个盒子的方案数,盒子可以为空。

由于 \(y-1\) 个板隔开 \(y\) 段,那么 \(y\) 个盒子等价于 \(y-1\) 个隔板。

画出 \(x+y-1\) 个位置,在这 \(x+y-1\) 个位置里面填入 \(y-1\) 个板子,剩下的位置放球。

如果板子是相邻的就说明这两块板隔开的盒子为空,反之这个盒子中球的个数就是这相邻两个板子中间的球的数量。

也就是说,“\(x\) 个球放进 \(y\) 个盒子的方案数,盒子可以为空”的方案数为 \(C(x+y-1,y-1)\) 或者 \(C(x+y-1,x)\)


回到这道题,由于题目要求 “不超过”,那么我们定义第 \(m+1\) 个盒子为垃圾箱,加入我们在 \(m\) 个盒子里面放入 \(m'(m'\le m)\) 个球,那么垃圾箱里面就会放 \(m-m'\) 个球。

通俗地讲,多余的球会放入垃圾箱。

那么,这道题就被我们转化为 “在 \(m+1\) 个盒子里面放入 \(n\) 个球”。

由于之前的推导,我们易知此题答案为 \(C(n+m,m)\)

#include<cstdio>

#define int long long

const int MAXP=100000;

int T,n,m,p;

int fac[MAXP+5],inv[MAXP+5];

inline int INV(int a,const int mod){
	int ret=1,n=mod-2;
	for(;n>0;n>>=1){
		if(n&1)ret=ret*a%mod;
		a=a*a%mod;
	}
	return ret;
}

inline int C(const int n,const int m,const int p){
	if(n<m)return 0;
	return fac[n]*INV(fac[m],p)%p*INV(fac[n-m],p)%p;
}

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

inline void init(const int lim){
	fac[0]=fac[1]=inv[1]=1;//注意 fac[0]=1, 因为可能要用到 0 的阶乘
	for(int i=2;i<=lim;++i)fac[i]=fac[i-1]*i%p;
	// for(int i=2;i<=lim;++i)inv[i]=inv[p%i]*(p-p/i)%p;
	// for(int i=2;i<=lim;++i)inv[i]=inv[i]*inv[i-1]%p;
}

signed main(){
	scanf("%lld",&T);
	while(T--){
		scanf("%lld%lld%lld",&n,&m,&p);
		init(p);
		printf("%lld\n",lucas(n+m,n,p));
	}
	return 0;
}
posted @ 2020-04-22 21:21  Arextre  阅读(86)  评论(0编辑  收藏  举报