$\newcommand{\align}[1]{\begin{align*}#1\end{align*}}$题意:对于一个排列$p_{1\cdots n}$构造一个图,如果$i\lt j$且$p_i\lt p_j$,就连双向边$(i,j)$,这个排列的权值就是所有连通块的大小的乘积,求所有$n!$个排列的权值总和
考虑DP,设$f_n$表示(长度为$n$的排列构成的图)连通的方案数,则我们可以用$n!$减去不连通的方案数,考虑把不合法的方案按第一个连通块大小$i$分类,则后$n-i$个数可以随意乱排,所以$\align{f_n=n!-\sum\limits_{i=1}^{n-1}(n-i)!f_i}$
设答案为$g_n$,枚举最后一个连通块的大小$i$,我们得到转移$\align{g_n=\sum\limits_{i=1}^nif_ig_{n-i}}$
这样做是$O(n^2)$的,考虑优化
先转化一下$f$的递推式,$\align{2n!=[n=0]+\sum\limits_{i=0}^n(n-i)!f_i}$
设$\align{A(x)=\sum\limits_ii!x^i,B(x)=\sum\limits_if_ix^i}$,则$2A(x)=A(x)B(x)+1$,即$B(x)=\dfrac{2A(x)-1}{A(x)}$
直接多项式求逆可以算出$B(x)$,设$\align{C(x)=\sum\limits_iif_ix^i}$,现在考虑算答案,枚举答案中连通块的个数$k$,答案为$\align{[x^n]\sum\limits_{i=1}^\infty C^k(x)=[x^n]\left(\dfrac1{1-C(x)}-1\right)}$,算出$C(x)$再求逆就可以了
但是毒瘤yww把它出到$n\leq5\times10^5$,这样做如果写的丑会T掉,所以我们来卡卡常==
首先注意到$C(x)$其实就是$B(x)$的每一项系数多了一个$i$,所以$C(x)=xB'(x)$,再利用$B(x)=\dfrac{2A(x)-1}{A(x)}$,我们得到$\dfrac1{1-C(x)}=\dfrac{A^2(x)}{A^2(x)-xA'(x)}$,优化到了只用两次FFT和一次多项式求逆,已经可以过了
还可以继续优化,我们需要找到快速计算$\align{h_n=[x^n]A^2(x)=\sum\limits_{i=0}^ni!(n-i)!}$的方法,在OEIS上可以找到递推公式$h_n=n!+\dfrac{n+1}2h_{n-1}$,但我并没有找到证明,看看以后有没有机会填坑,这样做就可以只做一次多项式求逆
#include<stdio.h>
#include<string.h>
const int mod=998244353,maxn=1048576;
typedef long long ll;
int mul(int a,int b){return a*(ll)b%mod;}
int ad(int a,int b){return(a+b)%mod;}
int de(int a,int b){return(a-b)%mod;}
int pow(int a,int b){
int s=1;
while(b){
if(b&1)s=mul(s,a);
a=mul(a,a);
b>>=1;
}
return s;
}
int rev[maxn],N,iN;
void pre(int n){
int i,k;
for(N=1,k=0;N<n;N<<=1)k++;
for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
iN=pow(N,mod-2);
}
void swap(int&a,int&b){a^=b^=a^=b;}
void ntt(int*a,int on){
int i,j,k,t,w,wn;
for(i=0;i<N;i++){
if(i<rev[i])swap(a[i],a[rev[i]]);
}
for(i=2;i<=N;i<<=1){
wn=pow(3,(on==1)?(mod-1)/i:(mod-1-(mod-1)/i));
for(j=0;j<N;j+=i){
w=1;
for(k=0;k<i>>1;k++){
t=mul(w,a[i/2+j+k]);
a[i/2+j+k]=de(a[j+k],t);
a[j+k]=ad(a[j+k],t);
w=mul(w,wn);
}
}
}
if(on==-1){
for(i=0;i<N;i++)a[i]=mul(a[i],iN);
}
}
int t[maxn];
void getinv(int*a,int*b,int n){
if(n==1){
b[0]=pow(a[0],mod-2);
return;
}
int i;
getinv(a,b,n>>1);
pre(n<<1);
memset(t,0,N<<2);
memcpy(t,a,n<<2);
ntt(t,1);
ntt(b,1);
for(i=0;i<N;i++)b[i]=mul(b[i],2-mul(b[i],t[i]));
ntt(b,-1);
for(i=n;i<N;i++)b[i]=0;
}
int a[maxn],b[maxn],c[maxn];
int main(){
int n,k,i,ans;
scanf("%d",&n);
for(k=1;k<=n;k<<=1);
a[0]=1;
for(i=1;i<=n;i++){
a[i]=mul(i,a[i-1]);
b[i]=mul(i,a[i]);
}
pre(k<<1);
ntt(a,1);
for(i=0;i<N;i++)a[i]=mul(a[i],a[i]);
ntt(a,-1);
for(i=0;i<=n;i++)b[i]=de(a[i],b[i]);
getinv(b,c,k);
ans=0;
for(i=0;i<=n;i++)ans=ad(ans,mul(a[i],c[n-i]));
printf("%d",ad(ans,mod));
}