counting
Description
数学老师走啦,英语老师来上课啦
他的性格与众不同,又因为大家都是理科班的学生
他希望大家在数字母的过程中领悟英语的快乐
他用\(m\)种字母进行排列组合,
得到了所有不同的,长度为\(n\)的字符串
(不需要所有字母都出现在字符串中)
对于每个字符串\(s\)
定义\(C(s)\)为\(s\)中出现次数最多的字母的出现次数
那么问题来了
所有的这些字符集大小为\(m\),长度为\(n\)的字符串中
\(C(s)=k\)的有多少个呢
Input
一行三个整数\(n,m,k\),分别表示字符集,长度和要求的\(C(s)\)
Output
输出一行表示结果
答案对\(998244353\)取模
Sample Input
3 2 2
Sample Output
6
HINT
数据保证\(k≤n\)
对于10%的数据,\(1≤n,m≤8\)
对于30%的数据,\(1≤n,m≤200\)
对于50%的数据,\(1≤n,m≤1000\)
对于100%的数据,\(1≤n,m≤50000\)
样例解释:
假设样例中的两个字母为\(a,b\)
则满足条件的有\(aab,aba,abb,baa,bab,bba\)六个
Solution
先考虑一下大力dp,\(f[i][j][k]\)表示确定完前\(i\)个字母的位置,占用了\(s\)中的\(j\)个位置,且此时\(C(s)<=k\)的方案数
那么最后的答案应该是\(f[m][n][k]-f[m][n][k-1]\)
仔细观察一下这个dp式子,会发现这个dp其实跟\(k\)没有太大的关系(影响的只是枚举范围),而\(i\)这一维通过转移时候的继承是可以直接省掉的,然后又发现这个是个卷积的形式,那就大力构造一下多项式然后乘一波咯
我们定义\(F_i(x)=\sum\limits_{j=0}^{k}\frac{f[i][j][k]}{j!}x^j\),\(G(x)=\sum\limits_{j=0}^{k}\frac{1}{j!}x^j\),那么上面式子的\(\frac{f[i][j][k]}{j!}\)就可以看做\(F_i(x)\)的\(j\)次项的系数(生成函数的意味。。?),其他的部分同理,我们可以得到这样的式子:
最后的\(f[m][n][k]\)就是:\(F_m(x)\)的\(n\)次项的系数*\(n!\),\(f[m][n][k-1]\)的计算同理,只要稍微改变一下\(k\)的值就好了,所以下面只讨论\(f[m][n][k]\)的求法:
重新看回\(F_i(x)\)的那个递推式,不难发现其实\(F_m(x)=(F_0(x)G(x))^m\)(一路推上来的嘛)
那就是做一个快速幂卷积就好了,注意一下每次乘完之后超过\(n\)的部分要赋成\(0\)
(初始化的话因为只有\(f[0][0][k]=1\),所以\(F_0(x)=1\))
代码大概长这个样子(NTT忘记swap这个错能犯一万年。。。菜菜发抖qwq)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define G 3
#define vct vector<int>
using namespace std;
const int MAXN=5*(1e4)+10,MOD=998244353;
int ksm(int x,int y);
namespace NTT{/*{{{*/
int W[MAXN*4][2],rev[MAXN*4];
int len,invlen,invg;
void init(){
invg=ksm(G,MOD-2);
for (int i=0;i<=17;++i){
W[1<<i][0]=ksm(G,(MOD-1)/(1<<i));
W[1<<i][1]=ksm(invg,(MOD-1)/(1<<i));
}
}
void prework(int n){
int bit=0;
for (len=1;len<n;len<<=1,++bit);
rev[0]=0;
for (int i=1;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
invlen=ksm(len,MOD-2);
}
void ntt(int *a,int op){
int w,w_n,u,t;
for (int i=0;i<len;++i)
if (rev[i]>i) swap(a[i],a[rev[i]]);
for (int step=2;step<=len;step<<=1){
w_n=op==-1?W[step][0]:W[step][1];
for (int st=0;st<len;st+=step){//< !!
w=1;
for (int i=0;i<(step>>1);++i){
t=1LL*a[st+i+(step>>1)]*w%MOD;
u=a[st+i];
a[st+i]=(1LL*u+t)%MOD;
a[st+i+(step>>1)]=(1LL*u+MOD-t)%MOD;
w=1LL*w*w_n%MOD;
}
}
}
if (op==1) return;
for (int i=0;i<len;++i)
a[i]=1LL*a[i]*invlen%MOD;
}
}/*}}}*/
int fac[MAXN],invfac[MAXN];
int base[MAXN*4],ret[MAXN*4];
int n,m,k;
void prework(int n);
void ksm_solve(int *a,int n,int y,int *ret);//n=len m=num k=appear times
int solve(int n,int m,int k);
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d%d%d",&n,&m,&k);
prework(n);
int ansk,ansk1;
ansk1=solve(n,m,k-1);
ansk=solve(n,m,k);
printf("%d\n",(1LL*ansk+MOD-ansk1)%MOD);
}
int ksm(int x,int y){
int ret=1,base=x;
for (;y;y>>=1,base=1LL*base*base%MOD)
if (y&1) ret=1LL*ret*base%MOD;
return ret;
}
void prework(int n){
NTT::init();
NTT::prework((n+1)*2);
fac[0]=1;
for (int i=1;i<=n;++i) fac[i]=1LL*fac[i-1]*i%MOD;
invfac[n]=ksm(fac[n],MOD-2);
for (int i=n-1;i>=0;--i) invfac[i]=1LL*invfac[i+1]*(i+1)%MOD;
}
int solve(int n,int m,int k){
memset(base,0,sizeof(base));
memset(ret,0,sizeof(ret));
for (int i=0;i<=k;++i) base[i]=invfac[i];
ksm_solve(base,n,m,ret);
return 1LL*ret[n]*fac[n]%MOD;
}
void ksm_solve(int *a,int n,int y,int *ret){
ret[0]=1;
while (y){
NTT::ntt(a,1);
if (y&1){
NTT::ntt(ret,1);
for (int i=0;i<NTT::len;++i) ret[i]=1LL*ret[i]*a[i]%MOD;
NTT::ntt(ret,-1);
for (int i=n+1;i<NTT::len;++i) ret[i]=0;
}
for (int i=0;i<NTT::len;++i) a[i]=1LL*a[i]*a[i]%MOD;
NTT::ntt(a,-1);
for (int i=n+1;i<NTT::len;++i) a[i]=0;
y>>=1;
}
}