题解:
放完前$i-1$个数之后,$i$会让前面的数变成一个整体,而且与后面没有影响。
就有了$dp$方程:$$dp[i]=sum(k^2*dp[i-k]*(k-1)!*C(i-1,k-1))$$
拆开组合数之后有这个东西:$$frac{dp[i]}{(i-1)!}=sum(frac{dp[i-k]}{(i-k)!}*k^2)$$
于是就开心的去写$CDQ$了。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MOD = 998244353; const int N = 150000; typedef long long ll; ll fastpow(ll x,int y) { ll ret = 1; while(y) { if(y&1)ret=ret*x%MOD; x=x*x%MOD; y>>=1; } return ret; } int to[4*N],lim=1,L=0; ll inv,W[4*N]; void ntt(ll *a,int len,int k) { for(int i=0;i<len;i++) if(i<to[i])swap(a[i],a[to[i]]); for(int i=1;i<len;i<<=1) { ll w0 = W[i]; for(int j=0;j<len;j+=(i<<1)) { ll w = 1; for(int o=0;o<i;o++,w=w*w0%MOD) { ll w1 = a[j+o],w2 = a[j+o+i]*w%MOD; a[j+o] = (w1+w2)%MOD; a[j+o+i] = (w1-w2+MOD)%MOD; } } } if(k==-1) { for(int i=1;i<(len>>1);i++)swap(a[i],a[len-i]); for(int i=0;i<len;i++)a[i]=a[i]*inv%MOD; } } ll jc[N],j2[N],jn[N]; ll dp[N]; ll a[4*N],b[4*N],c[4*N]; void divi(int l,int r) { if(l==r) { if(l)dp[l] = dp[l] * jc[l-1] % MOD; return ; } int mid = (l+r)>>1; divi(l,mid); lim = 1,L = 0; while(lim<=(r-l+1))lim<<=1,L++; for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(L-1))); inv = fastpow(lim,MOD-2); for(int i=1;i<lim;i<<=1)W[i]=fastpow(3,(MOD-1)/(i<<1)); for(int i=0;i<lim;i++)a[i]=b[i]=0; for(int i=0;i<=mid-l;i++)a[i]=dp[i+l]*jn[i+l]; for(int i=0;i<=r-l;i++)b[i]=j2[i]; ntt(a,lim,1),ntt(b,lim,1); for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD; ntt(c,lim,-1); for(int i=mid+1;i<=r;i++)dp[i]=(dp[i]+c[i-l])%MOD; divi(mid+1,r); } int main() { jc[0] = jn[0] = 1; for(int i=1;i<=100000;i++) { jc[i] = jc[i-1]*i%MOD; j2[i] = 1ll*i*i%MOD; jn[i] = fastpow(jc[i],MOD-2); } dp[0] = 1; divi(0,100000); int x; while(scanf("%d",&x)>0) printf("%I64d ",dp[x]); return 0; }