题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6397
题意:有n个数字0~n-1,问任取m个使得总和为k的方案数。(可以重复取)
解析:此题可以用母函数来解,也可以通过组合数容斥来解。
组合数容斥解法:
我们将问题转化为:X个相同的球放入Y个不相同的盒子中,要求每个盒子至多N个球至少0个球,共有多少种不同的放法?正好是知乎上的一个话题https://www.zhihu.com/question/65668587,图中X=k,Y=m,N=n-1;
(图片截图自知乎,侵删)
(以下题解来自dls直播,录屏重播地址https://www.bilibili.com/video/av29454041)
母函数解法:
- 首先可得母函数:(1+x^1+x^2+...+x^(n-1))^m,答案就是此多项式中x^k的系数。
- 由等比数列公式(1+x^1+x^2+...+x^(n-1)) = (1-x^n) / (1-x);则(1+x^1+x^2+...+x^(n-1))^m=(1-x^n)^m / (1-x)^m=(1-x^n)^m * (1-x)^-m;
- 由多项式定理(1-x^n)^m= sum( i=0 to m (-1)^i*C(m,i)*x^(i*n) );
- 又由某公式得(1-x)^-m= sum( i>=0 x^i*C(m-1+i,m-1) );
- 母函数可化为:sum( i=0 to m (-1)^i*C(m,i)*x^(i*n) ) * sum( i>=0 x^i*C(m-1+i,m-1) );
- 此多项式中x^k的系数为:sum( i>=0&&i*n<=k (-1)^i * C(m,i) * C(m-1+k-n*i,m-1);
容斥解法:
- 由于问题转化为将总和k为m份,每份的值xi>=0&&xi<=n-1的方案数。
- 首先有经典组合公式:x1+x2+...+xm=k (xi>=0) 解的个数为C(k+m-1,m-1)。
- 那么我们相用如上公式解决本题,发现上述公式没有此题中xi<=n-1的条件,那我们就求出总方案,容斥掉不合法的方案(即存在xi>=n-1)。
- 对于有t个不合法的xi>=n(假设k>=t*n),我们怎么求这个不合法的方案数(即x1+x2+...+xm=k (存在xi>=n) 方案数),还是用经典组合公式,先做变换:如果xi<n则xi'=xi,否则xi'=xi-n,则x1+x2+...+xm=k (存在xi>=n) ==> x1'+x2'+...+xm'=k-t*n (所有xi>=0);方案数就是C(k-t*n+m-1,m-1)。
- 再进行容斥就是0个不合法 - 1个不合法 + 2个不合法 - 3个不合法。。。。
- 公式如下: 容斥系数 变量选法 c个不合法方案数
- -> c个不合法( c>=0&&c*n<=k ) (-1)^c * C(m,c) * C(k-c*n+m-1,m-1)
发现两种方法所得公式相同。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const ll M=2*1e5+5;
ll n,m,k;
ll fact[M],ifact[M];//fact[i]是i的阶乘,ifact[i]是阶乘的除法逆元,两者用于求组合数
ll pow_mod(ll n,ll k,ll mod)//快速幂求n^k余m的结果
{
ll res=1;
n=n%mod;
while(k>0)
{
if(k&1)
res=res*n%mod;
n=n*n%mod;
k>>=1;
}
return res;
}
void init()//初始化
{
fact[0]=ifact[0]=1;
for(int i=1;i<M;++i)
{
fact[i]=(fact[i-1]*i)%mod;
ifact[i]=pow_mod(fact[i],mod-2,mod);
}
ifact[M-1]=pow_mod(fact[M-1],mod-2,mod);
for(int i=M-1;i>0;i--)
ifact[i-1]=ifact[i]*i%mod;
}
ll C(ll n,ll m)//求组合数
{
if(n<m)
return 0;
return fact[n]*ifact[m]%mod*ifact[n-m]%mod;
}
int main()
{
init();
int T;
scanf("%d",&T);
while(T--)
{
scanf("%I64d%I64d%I64d",&n,&m,&k);
ll X=k,Y=m,N=n-1;
ll ans=C(X+Y-1,Y-1);
for(ll t=1;t<=X/(N+1);t++)
{
if((t&1)==0)
{
ans=(ans+C(Y,t)*C(X+Y-1-t*(N+1),Y-1)%mod)%mod;
}
else
{
ans=(ans-C(Y,t)*C(X+Y-1-t*(N+1),Y-1)%mod+mod)%mod;
}
}
printf("%I64d\n",ans%mod);
}
return 0;
}