题意:解方程: x1 + x2 +x3+x4+x5 + ...+xm = k( xi < n && xi > 0 )
坑点:这里取模犯了两个错误,首先,if( x > mod ) x-=mod;是错误的,正确的应该是含有等号的。其次,传入的参数一定保证取模之后再传入,否则加上一个mod是不够的。
思路1:容斥原理 先算没有上限的,再用容斥原理减去所有不合格的。( 具体来时就是,减去至少有一段大于等于n的所有可能情况数,加上所有至少有两段大于等于n的情况数。。。 )PS:注意是所有,这点很重要。
这题我一开始犯了一个愚蠢的错误就是去枚举不合格的那部分的具体情况数,实际上只需要将总数减去 不合格段数*n,剩下的直接隔板法就可以了。
#include <bits/stdc++.h>
typedef long long LL;
typedef long long ll;
const int maxn =1002000;
const ll oo=998244353;
LL ad( LL x,LL y ){
x += y;
if(x>=oo ) x-=oo;
if( x<0 ) x+= oo;
return x;
}
ll frac[maxn], carf[maxn];
ll inv(ll x){
if (x==1) return 1;
else return (oo-oo/x)*inv(oo%x)%oo;
}
void init(){
frac[0]=1;
for (int i=1;i<=1000000;i++)
frac[i]=frac[i-1]*i%oo;
carf[1000000]=inv(frac[1000000]);
for (int i=1000000;i>=1;i--)
carf[i-1]=carf[i]*i%oo;
}
ll C(int n, int m){
if (m>n) return 0;
else return frac[n]*carf[n-m]%oo*carf[m]%oo;
}
int main(){
init();
int T,n,m,k;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&k);
LL ans = C( k+m-1,m-1 );
int p =1;
int h = k/n;
for( int i = 1;i <= h;i++ ){
p*=-1;
LL v = 1LL*p*C(m,i)%oo*C( k-i*n+m-1,m-1 )%oo;
ans = ad( ans,v );
}
printf("%I64d\n",ans);
}
return 0;
}
思路2:生成函数裸题,用到广义二项式展开定理进行求解
#include <bits/stdc++.h>
typedef long long LL;
typedef long long ll;
#define maxn 1002000
const ll oo= 998244353 ;
ll frac[maxn], carf[maxn];
ll inv(ll x){
if (x==1) return 1;
else return (oo-oo/x)*inv(oo%x)%oo;
}
void init(){
frac[0]=1;
for (int i=1;i<=1000000;i++)
frac[i]=frac[i-1]*i%oo;
carf[1000000]=inv(frac[1000000]);
for (int i=1000000;i>=1;i--)
carf[i-1]=carf[i]*i%oo;
}
ll C(int n, int m){
if (m>n) return 0;
else return frac[n]*carf[n-m]%oo*carf[m]%oo;
}
int ad( LL x,LL y ){
x += y;
if( x>=oo ) x -= oo;
if( x < 0 ) x+= oo;
return x;
}
int main(){
int T,n,m,k;
init();
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&k);
LL ans = 0;
for( int k2 = 0;k2 <= m;k2++ ){
if( n*k2 > k ) break;
int k1 = k-k2*n;
LL v = C( m+k1-1,k1 )*C(m,k2)*(k2&1?-1:1);
ans = ad( ans,C( m+k1-1,k1 )*C(m,k2)%oo*(k2&1?-1:1) );
}
printf("%I64d\n",ans);
}
return 0;
}