P5748 集合划分计数 题解
考虑先对所有的 \(n\) 求出答案,每次询问 \(O(1)\) 输出。
记 \(f_n\) 表示 \(n\) 的答案,根据题意易得:
\[f_n=\sum_{i=1}^n {n-1\choose i-1}f_{n-i}
\]
组合数展开并分离系数得:
\[f_n=\sum_{i=1}^n \frac{(n-1)!}{(i-1)!}\times \frac{f_{n-i}}{(n-i)!}
\]
\[\frac{f_n}{(n-1)!}=\sum_{i=1}^n \frac{f_{n-i}}{(n-i)!}\times \frac{1}{(i-1)!}
\]
令 \(g_n=\frac{f_n}{(n-1)!}\),得:
\[g_n=\sum_{i=1}^n \frac{g_{n-i}}{n-i}\times \frac{1}{(i-1)!}
\]
这是一个卷积的形式,分治 NTT 即可。时间复杂度 \(O(n\log^2n)\)。
参考代码:
#include<bits/stdc++.h>
#define int long long
#define mxn 524288
#define md 998244353
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
int power(int x,int y){
int ans=1;
for(;y;y>>=1){
if(y&1)ans=ans*x%md;
x=x*x%md;
}
return ans;
}
int T,n,dp[mxn],f[mxn],g[mxn],rev[mxn],fac[mxn],ifac[mxn];
void ntt(int *a,int n,int flag){
rept(i,0,n)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int h=1;h<n;h<<=1){
int x,y,s=power(3,499122176/h);
for(int j=0;j<n;j+=h<<1){
int w=1;
for(int k=j;k<j+h;++k){
x=a[k],y=w*a[k+h]%md;
a[k]=(x+y)%md;
a[k+h]=(x-y+md)%md;
w=w*s%md;
}
}
}
if(flag==-1){
int p=power(n,md-2);
reverse(a+1,a+n);
rept(i,0,n)a[i]=a[i]*p%md;
}
}
void solve(int l,int s,int k){
if(s==1)return;
solve(l,s>>1,k-1);
rept(i,0,s>>1){
f[i]=l+i?dp[l+i]*ifac[l+i]%md*fac[l+i-1]%md:1;
g[i]=ifac[i];
}
rept(i,s>>1,s)f[i]=0,g[i]=ifac[i];
rept(i,0,s)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
ntt(f,s,1);ntt(g,s,1);
rept(i,0,s)f[i]=f[i]*g[i]%md;
ntt(f,s,-1);
rept(i,s>>1,s)dp[l+i]=(dp[l+i]+f[i-1])%md;
solve(l+(s>>1),s>>1,k-1);
}
signed main(){
fac[0]=1;
rep(i,1,131072)fac[i]=fac[i-1]*i%md;
ifac[131072]=power(fac[131072],md-2);
drep(i,131072,1)ifac[i-1]=ifac[i]*i%md;
dp[0]=1;
solve(0,131072,17);
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);
printf("%lld\n",dp[n]*fac[n-1]%md);
}
return 0;
}