版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yz467796454/article/details/81709195
题目链接:hdu 6397 Character Encoding
Sample Input
4
2 3 3
2 3 4
3 3 3
128 3 340
Sample Output
1
0
7
903
题意:给n,m,k,从0到n-1中取正好m个数字(可以重复取),求这m个数字之和为k的方案数
思路:假设没有0到n-1这个限制条件,那么问题就变成k分为m份有几种方案,这是经典的组合数学问题,用隔板法,有种方案数。当k小于等于n-1的时候,由于组合中不可能存在一个大于n-1的数字,所以答案就是,当k大于n-1的时候,我们需要去掉方案中含有n到k的数字的情况,这就需要用到容斥。容斥原理可以学习这篇博客,我也是看了这里的部分内容做这题的容斥原理详解。
我们假设为i个数大于n-1的情况,与上述同理,已经有i*n个位置被占用了,所以
那么最终结果即为
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#define maxn 2000006
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll b[maxn],inv[maxn],invf[maxn];
ll f[maxn];
int num[maxn];
ll sum[maxn];
ll mo(ll a,ll pp){
if(a>=0&&a<pp)return a;
a%=pp;
if(a<0)a+=pp;
return a;
}
ll mo(ll a){
if(a>=0&&a<mod)return a;
a%=mod;
if(a<0)a+=mod;
return a;
}
ll powmod(ll a,ll b,ll pp){
ll ans=1;
for(;b;b>>=1,a=mo(a*a,pp)){
if(b&1)ans=mo(ans*a,pp);
}
return ans;
}
ll powmod(ll a,ll b){
ll ans=1;
for(;b;b>>=1,a=mo(a*a)){
if(b&1)ans=mo(ans*a);
}
return ans;
}
ll C(int n,int m){
if(n<m)return 0;
if(m==n||m==0)return 1;
return b[n]*invf[n-m]%mod*invf[m]%mod;
}
void init(){
b[0]=1;
for(int i=1;i<maxn;i++)b[i]=b[i-1]*i%mod;
inv[1]=1;//逆元
for(int i=2;i<maxn;i++)inv[i]=((mod-mod/i)*inv[mod%i])%mod;
invf[0]=1;//累乘逆元
for(int i=1;i<maxn;i++)invf[i]=(invf[i-1]*inv[i])%mod;
f[0]=0;f[1]=1;f[2]=1;
for(int i=3;i<maxn;i++)f[i]=(f[i-1]+f[i-2])%(mod-1);
}
ll inv1(ll b){
return powmod(b,mod-2,mod);
}
ll inv2(ll a){
if(a==1)return 1;
return inv2(mod%a)*inv2(mod-mod/a)%mod;
}
int main(){
int t;
scanf("%d",&t);
init();
while(t--){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
ll tot=C(k+m-1,m-1)%mod;
if(k<=(n-1)){
printf("%lld\n",tot);
}
else{
for(int i=1;i<=m;i+=2){
if(k-i*n<0)break;
tot=(tot-C(m,i)%mod*C(k-i*n+m-1,m-1)%mod+mod*mod)%mod;
if(k-(i+1)*n<0)break;
if((i+1)>m)break;
tot=(tot+C(m,(i+1))%mod*C(k-(i+1)*n+m-1,m-1)%mod)%mod;
}
printf("%lld\n",tot);
}
}
return 0;
}