题目
m 个位置,每个位置填入范围 0 至 n-1 的整数,使各位数字的和为 k ,问共有多少种情况。
n,m,k(1≤n,m≤1e5,0≤k≤1e5)
思路来源
HDU 6397 Character Encoding [ 容斥原理 + 组合数 ]-CSDN博客
题解
的解,
等价于,也就是,
考虑xi是一个满足xi>n的方案数的集合,在平面上有x1,...,xn一共n个集合
对于,至少有i个数大于n的情形,
先从m个数里选i个数,C(m,i),再在等式右边把这i个数大于n的部分减去,使之满足xi大于等于0的要求,
所以对应这些的方案数是,方案数是
选这i个数的时候,不管剩下m-i个数,这样如果x1==n+1,x2==n+1的情况,
会在i==1且x1被选中的时候算一次,i==1且x2被选中的时候算一次,
所以,算有1个数超过的时候,算的实际上是至少有一个数超过的情形,
此时2个数同时超过的时候,在一个数超过的情况中被算了两次,3个数同时超过被算了3次,以此类推
那就是容斥原理了,统计恰有i个集合相交时,奇加偶减
由于最后是用总数即i==0的情形减去上述情况,那么就考虑上i==0之后,奇减偶加即可
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=2e5;
ll Finv[maxn+5],jc[maxn+5];
ll modpow(ll x,ll n,ll mod)
{
ll res=1;
for(;n;x=x*x%mod,n/=2)
if(n&1)res=res*x%mod;
return res;
}
void init()
{
jc[0]=Finv[0]=1;
for(int i=1;i<=maxn;++i)
{
jc[i]=jc[i-1]*i;
if(jc[i]>=mod)jc[i]%=mod;
}
Finv[maxn]=modpow(jc[maxn],mod-2,mod);
for(int i=maxn-1;i>=1;--i)
{
Finv[i]=Finv[i+1]*(i+1);
if(Finv[i]>=mod)Finv[i]%=mod;
}
}
ll C(int n,int m)
{
if(m<0||m>n)return 0;
return jc[n]*Finv[n-m]%mod*Finv[m]%mod;
}
int t,n,m,k;
ll ans;
int main()
{
init();
scanf("%d",&t);
while(t--)
{
ans=0;
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<=min(k/n,m);++i)
{
if(i&1)ans=(ans-C(m,i)*C(k-i*n+m-1,m-1)%mod+mod)%mod;
else ans=(ans+C(m,i)*C(k-i*n+m-1,m-1)%mod+mod)%mod;
}
printf("%lld\n",ans);
}
return 0;
}