首先我们考虑,有个,那么最多能填满的瓶子的个数就是
很容易发现,当为奇数的时候,最多能填满的瓶子个数和时是一样的,所以多的那一个珍珠就被“浪费”了。那么设。那么最多能填满的个数就是,方案合法当且仅当,也就是说。
特判一下,显然有时,时。
令为有个元素个数(即)为奇数的方案数。则最后的答案就是。
记 表示 “钦定(指定) 个作为奇数”, 表示 “恰好选 个”,则对于任意的 , 在 中被计算了 次,故 ,其中 是数目上界。
**注意:在定义中, 表示先钦定(指定) 个为奇数,其他的随意选,再统计钦定情况如此的方案数,其中会包含重复的方案,因为一个方案可以有多种钦定情况。具体地,对于恰好选择 个,钦定情况数为 ,故 在 中被计算了 次。切勿将 来理解为普通的后缀和。**转自 二项式反演及其应用
二项式反演的结论就是:
那么有。
然后二项式反演一波:
因为我们想要两个变量(,)的和为一个定值(只和相关),所以我们令,然后把代入进去即可。最后的值的位置就在这个位置。
我们把问题转换成求。
因为是指钦定选个,其他随意的方案数。也就是说我们先枚举组合数,然后计算这个选奇数,其余的随意的方案数。因为奇数的是。这里之所以要用是因为在这道题里面,生成的随机序列是个排列,顺序不同也算不同的方案数。所以我们枚举了每种元素有多少个后,还要重新给每个元素编号确定位置,所以最后要乘上一个全排列。又因为同一种元素之间的相对顺序进行排列是没有意义的(同种球是完全相同的)。所以我们就要用做一个可重排列(除掉每种元素个数的阶乘即可)。(钟神曾经说过,可以理解成多一个系数)
那么写一下柿子:
中间那一步就是二项式反演:,表示多项式的第项。
换元法搞一下,,所以有。
带回去,将枚举换成枚举:
这玩意也是一个卷积。然后就可以求啦~求完了按照上个部分的柿子再卷积一下求出就能直接求答案了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#define vi vector<int>
#define pb push_back
#define mk make_pair
#define pii pair<int,int>
#define rep(i,a,b) for(int i=(a),i##end=(b);i<=i##end;i++)
#define fi first
#define se second
typedef long long ll;
using namespace std;
const int mod=998244353;
const int maxn=4*2e5+10000;
ll frac[maxn];
ll ksm(ll num,ll t){
ll res=1;num%=mod;
for(;t;t>>=1,num=num*num%mod){
if(t&1)res=num*res%mod;
}
return res;
}
struct NTT{
ll tmp1[maxn],tmp2[maxn],wn[2][maxn];
int rev[maxn];
void get_wn(int len){
for(int l=2;l<=len;l<<=1){
ll w1=ksm(3,(mod-1)/l),w0=ksm(3,mod-1-(mod-1)/l);
wn[0][l>>1]=wn[1][l>>1]=1;
for(int j=1;j<(l>>1);j++){
wn[0][(l>>1)+j]=wn[0][(l>>1)+j-1]*w0%mod;
wn[1][(l>>1)+j]=wn[1][(l>>1)+j-1]*w1%mod;
}
}
}
NTT(){int top=maxn/2;int len=1,cnt=0;while(len<=top)len<<=1,cnt++;get_wn(len);}
void get_rev(int cnt){rep(i,1,1<<cnt)rev[i]=(rev[i>>1]>>1)|((i&1)<<(cnt-1));}
void ntt(int len,ll *c,int f){
for(int i=0;i<len;i++)if(rev[i]>i)swap(c[rev[i]],c[i]);
for(int l=2;l<=len;l<<=1){
for(int i=0;i+l<=len;i+=l){
for(int j=i;j<i+(l>>1);j++){
ll tmp0=c[j],tmp1=1ll*c[j+(l>>1)]*wn[f][j-i+(l>>1)]%mod;
c[j]=(tmp0+tmp1)%mod;
c[j+(l>>1)]=((tmp0-tmp1)%mod+mod)%mod;
}
}
}
if(!f){
ll inv=ksm(len,mod-2);
rep(i,0,len-1)c[i]=c[i]*inv%mod;
}
}
vi mul(const vi &a,const vi &b){
int l1=a.size(),l2=b.size();int len=1,cnt=0;while(len<=l1+l2)len<<=1,cnt++;
get_rev(cnt);
rep(i,0,len)tmp1[i]=tmp2[i]=0;
rep(i,0,l1-1)tmp1[i]=a[i];
rep(i,0,l2-1)tmp2[i]=b[i];
ntt(len,tmp1,1);ntt(len,tmp2,1);
for(int i=0;i<len;i++)tmp1[i]=tmp1[i]*tmp2[i]%mod;
ntt(len,tmp1,0);
vi re;
for(int i=0;i<l1+l2;i++)re.pb(tmp1[i]);
return re;
}
}t;
int main(){
//freopen("in","r",stdin);
frac[0]=frac[1]=1;rep(i,2,maxn-1)frac[i]=frac[i-1]*i%mod;
int D,n,m;scanf("%d%d%d",&D,&n,&m);
if(2*m<=n-D){printf("%lld",ksm(D,n)%mod);return 0;}
if(2*m>n){printf("0");return 0;}
vi tmp1,tmp2;
rep(j,0,D){
tmp1.pb(ksm(-1,j)*ksm(D-2*j,n)%mod*ksm(frac[j],mod-2)%mod);
tmp2.pb(ksm(frac[j],mod-2));
}
vi f1=t.mul(tmp1,tmp2);vi f2=f1;vi G;G.resize(f2.size());
rep(i,0,D)f1[i]=1ll*f1[i]*frac[D]%mod*ksm(ksm(2,i)*frac[D-i]%mod,mod-2)%mod;
//cout<<">>f1 "<<f1[0]<<' '<<f1[1]<<' '<<f1[2]<<endl;
rep(i,0,D)f2[i]=1ll*f1[D-i]*frac[D-i]%mod;
//cout<<">>f2 "<<f2[0]<<' '<<f2[1]<<' '<<f2[2]<<endl;
rep(i,0,D)G[i]=1ll*ksm(-1,i)*ksm(frac[i],mod-2)%mod;
//cout<<">>G "<<G[0]<<' '<<G[1]<<' '<<G[2]<<endl;
vi ans=t.mul(f2,G);
rep(i,0,D)ans[i]=1ll*ans[i]*ksm(frac[D-i],mod-2)%mod;
ll out=0;
rep(i,0,n-2*m)out+=ans[D-i];
cout<<out%mod;
return 0;
}