[BZOJ4555][Tjoi2016&Heoi2016]求和(第二类斯特林数+NTT)

卷积太巨了!

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=4555

Solution

分析后得出结论:题目中给出的第二类斯特林数递推公式是没用的
首先要知道第二类斯特林数的通项公式:

S ( i , j ) = 1 j ! k = 0 j ( 1 ) k × ( j k ) i × C j k

证明: https://blog.csdn.net/xyz32768/article/details/80818366
然后,
f ( n ) = i = 0 n j = 0 i 2 j k = 0 j ( 1 ) k × ( j k ) i × C j k

j > i 时, S ( i , j ) = 0 。因此可以把 j = 0 i 换成 j = 0 n
i = 0 n j = 0 n 2 j k = 0 j ( 1 ) k × ( j k ) i × C j k

= j = 0 n 2 j k = 0 j ( 1 ) k j ! k ! × ( j k ) ! × i = 0 n ( j k ) i

Z ( m ) = i = 0 n m i ,那么:
Z ( m ) = { n + 1 m = 1 m n + 1 1 m 1 ELSE

那么,原式
= j = 0 n 2 j k = 0 j ( 1 ) k j ! k ! × ( j k ) ! × Z ( j k )

把只与 j k j k 的式子分开:
= j = 0 n 2 j × ( j ! ) k = 0 j ( 1 ) k k ! × Z ( j k ) ( j k ) !

F ( m ) = ( 1 ) m m ! G ( m ) = Z ( m ) m ! ,
那么容易看出这是一个卷积的形式:
f ( n ) = j = 0 n 2 j × ( j ! ) × ( F G ) ( j )

NTT 直接码上。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Pow(k, n) for (k = 1; k < n; k <<= 1)
#define Step(i, a, b, x) for (i = a; i <= b; i += x)
using namespace std;
const int N = 3e5 + 5, PYZ = 998244353;
int n, f[N], g[N], res[N], a[N], fac[N], in[N], inv[N], m = 1, cnt, ans,
rev[N], pw[N];
int qpow(int a, int b) {
    int res = 1; while (b) b & 1 ? res = 1ll * res * a % PYZ : 0,
        a = 1ll * a * a % PYZ, b >>= 1; return res;
}
void NTT(int *a, int g) {
    int i, j, k, r = cnt; For (i, 0, m - 1) if (i < rev[i]) swap(a[i], a[rev[i]]);
    pw[0] = g; For (i, 1, cnt - 1) pw[i] = 1ll * pw[i - 1] * pw[i - 1] % PYZ;
    Pow (k, m) {
        int x = pw[--r]; Step (i, 0, m - 1, k << 1) {
            int w = 1; For (j, 0, k - 1) {
                int u = a[i + j], v = 1ll * w * a[i + j + k] % PYZ;
                a[i + j] = (u + v) % PYZ; a[i + j + k] = (u - v + PYZ) % PYZ;
                w = 1ll * w * x % PYZ;
            }
        }
    }
}
int main() {
    int i, _g; cin >> n; fac[0] = 1;
    For (i, 1, n) fac[i] = 1ll * fac[i - 1] * i % PYZ;
    in[1] = 1; For (i, 2, n) in[i] = 1ll * (PYZ - PYZ / i) * in[PYZ % i] % PYZ;
    inv[n] = qpow(fac[n], PYZ - 2); Rof (i, n - 1, 0)
        inv[i] = 1ll * inv[i + 1] * (i + 1) % PYZ;
    For (i, 0, n) f[i] = 1ll * ((i & 1) ? PYZ - 1 : 1) * inv[i] % PYZ;
    g[g[0] = 1] = n + 1; For (i, 2, n) g[i] = 1ll * (qpow(i, n + 1) - 1)
        * in[i - 1] % PYZ * inv[i] % PYZ;
    a[0] = 1; For (i, 1, n) a[i] = 2ll * a[i - 1] % PYZ;
    For (i, 1, n) a[i] = 1ll * a[i] * fac[i] % PYZ;
    while (m <= (n << 1)) m <<= 1, cnt++; For (i, 0, m - 1)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << cnt - 1);
    _g = qpow(3, 119 * qpow(2, 23 - cnt)); NTT(f, _g); NTT(g, _g);
    For (i, 0, m - 1) res[i] = 1ll * f[i] * g[i] % PYZ;
    NTT(res, qpow(_g, PYZ - 2)); For (i, 0, n)
        ans = (ans + 1ll * a[i] *
        (1ll * res[i] * qpow(m, PYZ - 2) % PYZ) % PYZ) % PYZ;
    cout << ans << endl; return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/80885201