分治ntt
考虑从添加i,放在j位置,那么1->j是一个连通块,j+1->i和1->j不连通,那么我们可以列出式子dp[i]=∑j=1->i dp[i-j]*A(i-1,j-1)*j^2
dp[i]表示i个数的答案
然后化简一下就可以分治ntt了
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 3e5 + 5, P = 998244353; int n, k; ll a[N], b[N], fac[N], inv[N], facinv[N], dp[N]; ll power(ll x, ll t) { ll ret = 1; for(; t; t >>= 1, x = x * x % P) if(t & 1) ret = ret * x % P; return ret; } void ntt(ll *a, int f) { for(int i = 0; i < n; ++i) { int t = 0; for(int j = 0; j < k; ++j) if(i >> j & 1) t |= 1 << (k - j - 1); if(i < t) swap(a[i], a[t]); } for(int l = 2; l <= n; l <<= 1) { ll w = power(3, f == 1 ? (P - 1) / l : P - 1 - (P - 1) / l); int m = l >> 1; for(int i = 0; i < n; i += l) { ll t = 1; for(int k = 0; k < m; ++k, t = t * w % P) { ll x = a[i + k], y = t * a[i + m + k]; a[i + k] = (x + y) % P; a[i + m + k] = ((x - y) % P + P) % P; } } } if(f == -1) { ll inv = power(n, P - 2); for(int i = 0; i < n; ++i) a[i] = a[i] * inv % P; } } void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); n = 1; k = 0; while(n <= r - l + 1) n <<= 1, ++k; for(int i = 0; i < n; ++i) a[i] = b[i] = 0; for(int i = l; i <= mid; ++i) a[i - l] = dp[i] * facinv[i] % P; for(int i = 1; i <= r - l; ++i) b[i] = (ll)i * i % P; ntt(a, 1); ntt(b, 1); for(int i = 0; i < n; ++i) a[i] = a[i] * b[i] % P; ntt(a, -1); for(int i = mid + 1; i <= r; ++i) dp[i] = (dp[i] + a[i - l] * fac[i - 1] % P) % P; cdq(mid + 1, r); } int main() { inv[0] = inv[1] = facinv[0] = fac[0] = 1; for(int i = 1; i < N; ++i) { if(i != 1) inv[i] = (P - P / i) * inv[P % i] % P; facinv[i] = facinv[i - 1] * inv[i] % P; fac[i] = fac[i - 1] * i % P; } dp[0] = 1; cdq(0, 100000); while(scanf("%d", &n) != EOF) { printf("%lld\n", dp[n]); } return 0; }