考虑连通块是什么样的,对于 $n$ 的排列,$n$ 所在位置之前的点肯定都是 $n$ 连通块中的,并且 $n$ 没有往后的边。然后对于 $n$ 右边的数列也可以递归下去考虑,并且由于和前面无关,权值和显然等于后面长度排列的权值和。
定义 $f_i$ 表示 $i$ 排列的权值和,那么:
$$ f_i=\sum_{j=1}^{i}f_{i-j}\cdot j^2{(i-1)!\over (i-j)!}\\ {f_i\over (i-1)!}=\sum_{j=1}^{i}{f_{i-j}\over (i-j)!}j^2 $$
显然是分治NTT的形式。
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100000,maxt=1<<18,MOD=998244353;
int n,f[maxn+5],INV[maxn+5],fac[maxn+5];
int wn[maxt+5],A[maxt+5],B[maxt+5];
inline int ADD(int x,int y) {return x+y>=MOD?x+y-MOD:x+y;}
inline int MUL(int x,int y) {return (LL)x*y%MOD;}
int Pow(int w,int b) {int s;for (s=1;b;b>>=1,w=MUL(w,w)) if (b&1) s=MUL(s,w);return s;}
void NTTPre(){
int x=Pow(3,(MOD-1)/maxt);
wn[maxt>>1]=1;
for (int i=(maxt>>1)+1;i<maxt;i++) wn[i]=MUL(wn[i-1],x);
for (int i=(maxt>>1)-1;i;i--) wn[i]=wn[i<<1];
}
void NTT(int *a,int n,int f){
if (f>0){
for (int k=n>>1;k;k>>=1)
for (int i=0;i<n;i+=k<<1)
for (int j=0;j<k;j++){
int x=a[i+j],y=a[i+j+k];
a[i+j+k]=MUL(x+MOD-y,wn[k+j]);
a[i+j]=ADD(x,y);
}
} else {
for (int k=1;k<n;k<<=1)
for (int i=0;i<n;i+=k<<1)
for (int j=0;j<k;j++){
int x=a[i+j],y=MUL(a[i+j+k],wn[k+j]);
a[i+j+k]=ADD(x,MOD-y);
a[i+j]=ADD(x,y);
}
for (int i=0,INV=MOD-(MOD-1)/n;i<n;i++) a[i]=MUL(a[i],INV);
reverse(a+1,a+n);
}
}
void Make(int n){
INV[0]=INV[1]=1;for (int i=2;i<=n;i++) INV[i]=MUL(MOD-MOD/i,INV[MOD%i]);
fac[0]=1;for (int i=1;i<=n;i++) fac[i]=MUL(fac[i-1],i),INV[i]=MUL(INV[i-1],INV[i]);
}
void Solve(int L,int R){
if (L==R) {L==0?f[L]=1:f[L]=MUL(f[L],fac[L-1]);return;}
int mid=L+(R-L>>1);
Solve(L,mid);
int t;for (t=1;t<=R-L;t<<=1);
for (int i=L;i<=mid;i++) A[i-L]=MUL(f[i],INV[i]);for (int i=mid-L+1;i<t;i++) A[i]=0;
B[0]=0;for (int i=1;i<=R-L;i++) B[i]=MUL(i,i);for (int i=R-L+1;i<t;i++) B[i]=0;
NTT(A,t,1);NTT(B,t,1);
for (int i=0;i<t;i++) A[i]=MUL(A[i],B[i]);
NTT(A,t,-1);for (int i=mid+1;i<=R;i++) f[i]=ADD(f[i],A[i-L]);
Solve(mid+1,R);
}
int main(){
NTTPre();Make(maxn);Solve(0,maxn);
while (~scanf("%d",&n)) printf("%d\n",f[n]);
return 0;
}