有关斯特林数的介绍和斯特林反演:斯特林数及斯特林反演。
本来是想做容斥的,结果发现了一个多项式题目……
不过容斥和反演息息相关嘛。
这题做完之后感觉卷积也不是那么难,就把它理解成一个预处理一个复杂函数的方法就好了。这样复杂度可以从 (O(n)) 求和式变成 (O(1)) 取得函数值了。
这道题目是有关第二类斯特林数的,上文的博客中推导了第二类斯特林数的一个公式:
(egin{aligned} displaystyle S(n, m) = frac{1}{m!} sum_{k = 0}^m (-1)^k C(m, k) (m - k)^n \ displaystyle = sum_{k = 0}^m frac{(-1)^k (m - k)^n}{k!(m - k)!} end{aligned})
发现上式是个卷积形式,于是本题的柿子可以这样推导(后两步好神仙啊):
(egin{aligned} displaystyle F(n) =sum_{i=0}^n sum_{j=0}^i S(i, j) * 2^j * j! \ displaystyle =sum_{i=0}^n sum_{j=0}^n S(i, j) * 2^j*j! \ displaystyle =sum_{j=0}^n 2^j*j!sum_{i=0}^n S(i, j) \ displaystyle =sum_{j=0}^n 2^j*j!sum_{i=0}^n sum_{k=0}^jfrac{(-1)^k}{k!}cdotfrac{(j-k)^i}{(j-k)!} \ displaystyle =sum_{j=0}^n 2^j*j!sum_{k=0}^jfrac{(-1)^k}{k!}cdotfrac{ sum_{i=0}^n (j-k)^i}{(j-k)!} \ displaystyle =sum_{j=0}^n 2^j*j!sum_{k=0}^jfrac{(-1)^k}{k!}cdot frac{(j-k)^{n+1}-1}{(j-k-1)(j-k)!} \ end{aligned})
这样,这道题目的柿子也化成了卷积形式:
(egin{aligned} displaystyle f(x) = frac{(-1)^x}{x!} \ displaystyle g(x) = frac{x^{n+1}-1}{(x-1)*x!} \ displaystyle F(n) = sum_{i = 0}^{n} 2^i * i! *(f×g)(i) end{aligned})
可以用 (NTT) 求解 …… 抄抄抄。
#include <cmath>
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int mod = 998244353;
const int maxn = 400000 + 10;
int n, len, rado, limit, r[maxn], fac[maxn], inv[maxn], f[maxn], g[maxn], pow_2[maxn], ans;
inline int Fast_pow(int x, int p) {
register int res = 1;
for( ; p; x = 1ll * x * x % mod, p = p >> 1) if( p & 1 ) res = 1ll * x * res % mod;
return res;
}
inline void Fast_numbertheory_transform(int *a, int type) {
for(int i = 0; i < limit; ++i) if( i < r[i] ) swap(a[i], a[r[i]]);
for(int mid = 1; mid < limit; mid = mid << 1) {
int Base_p = Fast_pow(3ll, (mod - 1) / (mid << 1));
if( type == -1 ) Base_p = Fast_pow(Base_p, mod - 2);
for(int l = 0, length = mid << 1; l < limit; l = l + length) {
for(int k = 0, p = 1; k < mid; ++k, p = 1ll * p * Base_p % mod) {
int x = a[l + k], y = 1ll * p * a[l + mid + k] % mod;
a[l + k] = (x + y) % mod, a[l + mid + k] = (x - y + mod) % mod;
}
}
}
int inver = Fast_pow(limit, mod - 2);
if( type == -1 ) for(int i = 0; i < limit; ++i) a[i] = 1ll * a[i] * inver % mod;
}
int main(int argc, char const *argv[])
{
scanf("%d", &n), pow_2[0] = fac[0] = fac[1] = 1;
for(int i = 1; i <= n; ++i) pow_2[i] = (pow_2[i - 1] << 1) % mod;
for(int i = 2; i <= n; ++i) fac[i] = 1ll * i * fac[i - 1] % mod;
inv[n] = Fast_pow(fac[n], mod - 2);
for(int i = n; i >= 1; --i) inv[i - 1] = 1ll * inv[i] * i % mod;
g[0] = 1, g[1] = n + 1;
for(int i = 0; i <= n; ++i) f[i] = 1ll * ((i & 1) ? mod - 1 : 1) * inv[i] % mod;
for(int i = 2; i <= n; ++i) g[i] = 1ll * (Fast_pow(i, n + 1) - 1 + mod) * Fast_pow(i - 1 + mod, mod - 2) % mod * inv[i] % mod;
len = n << 1, rado = 0, limit = 1;
while( limit < len ) limit = (limit << 1), ++rado;
for(int i = 0; i < limit; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (rado - 1));
Fast_numbertheory_transform(f, 1);
Fast_numbertheory_transform(g, 1);
for(int i = 0; i < limit; ++i) f[i] = 1ll * f[i] * g[i] % mod;
Fast_numbertheory_transform(f, -1);
for(int i = 0; i <= n; ++i) ans = (ans + 1ll * fac[i] * pow_2[i] % mod * f[i] % mod) % mod;
printf("%d
", ans);
return 0;
}