[BZOJ]4555 [TJOI2016&HEOI2016] 求和 第二类斯特林数 + NTT

4555: [Tjoi2016&Heoi2016]求和

Time Limit: 40 Sec   Memory Limit: 128 MB
Submit: 525   Solved: 418
[ Submit][ Status][ Discuss]

Description

在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心。

现在他想计算这样一个函数的值:
S(i, j)表示第二类斯特林数,递推公式为:
S(i, j) = j ∗ S(i − 1, j) + S(i − 1, j − 1), 1 <= j <= i − 1。
边界条件为:S(i, i) = 1(0 <= i), S(i, 0) = 0(1 <= i)
你能帮帮他吗?

Input

输入只有一个正整数

Output

 输出f(n)。由于结果会很大,输出f(n)对998244353(7 × 17 × 223 + 1)取模的结果即可。1 ≤ n ≤ 100000

Sample Input

3

Sample Output

87

HINT

Source

[ Submit][ Status][ Discuss]


HOME Back

  这道题要是知道第二类斯特林数的通项公式就没有那么难了, 具体推导过程网上已经有很详尽了, 不细讲.

  说几个巧点. 一个是当和式上界处理麻烦的话, 可以直接提高到最高界看一下有无影响, 如果没有的话那么会极大的方便了推导过程. 比如这道题就是可以的. 因为第二类斯特林数当s(i, j)当j>i的时候显然为0. 另外一个是最好把与和式与其所枚举的项数移动到一起, 这样我们可以一起处理, 也方便讨论和发现一些美妙的性质.

  不过感觉第二类斯特林数总要跟NTT扯上关系啊... 毕竟通项中(m - k)^n太好搞事情了.

#include<bits/stdc++.h>
using namespace std;
typedef long long lnt;
const int g = 3;
const int maxn = 4e5 + 5;
const int mod = 998244353;
lnt ans;
int n, L, m;
int rev[maxn];
int a[maxn], b[maxn], c[maxn];
int fac[maxn], inv[maxn], w[maxn];
inline int mpow(int a, int b) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod, b >>= 1;
    }
    return ret;
}
inline void init() {
    register int i;
    fac[0] = 1;
    for (i = 1; i <= m; ++ i) fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[m] = mpow(fac[m], mod - 2);
    for (i = m - 1; ~i; -- i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
}
inline void NTT(int *a, int f) {
    register int i, j, k, l;
    for (i = 0; i < n; ++ i)
        if (rev[i] > i) swap(a[rev[i]], a[i]);
    for (i = 1, l = 2, w[0] = 1; i < n; i <<= 1, l <<= 1) {
        lnt wn = mpow(g, (f & 1) ? (mod - 1) / l : mod - 1 - (mod - 1) / l);
        for (j = 1; j < i; ++ j) w[j] = 1ll * w[j - 1] * wn % mod;
        for (j = 0; j < n; j += l)
            for (k = 0; k < i; ++ k) {
                int x = a[j + k], y = 1ll * w[k] * a[j + k + i] % mod;
                a[j + k] = (x + y >= mod) ? x + y - mod : x + y;
                a[j + k + i] = (x - y < 0) ? x - y + mod : x - y;
            }
    }
    if (!f) {
        lnt ni = mpow(n, mod - 2);
        for (int i = 0; i < n; ++ i) a[i] = 1ll * a[i] * ni % mod;
    }
}
int main() {
    scanf("%d", &m); init();
    register int i, j;
    for (n = 1; n <= m << 1; n <<= 1) ++ L;
    for (i = 0; i < n; ++ i)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (L - 1));
    for (i = 0; i <= m; ++ i)
        a[i] = (i & 1) ? -inv[i] + mod : inv[i];
    b[0] = 1, b[1] = m + 1;
    for (i = 2; i <= m; ++ i)
        b[i] = 1ll * (mpow(i, m + 1) - 1) * mpow(i - 1, mod - 2) % mod * inv[i] % mod;
    NTT(a, 1), NTT(b, 1);
    for (i = 0; i < n; ++ i) a[i] = 1ll * a[i] * b[i] % mod;
    NTT(a, 0);
    for (i = 0, j = 1; i <= m; ++ i, j = (j << 1) % mod)
        ans = (ans + 1ll * j * fac[i] % mod * a[i] % mod) % mod;
    printf("%lld\n", ans);
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值