Siyuan

「TJOI / HEOI 2016」求和
「TJOI / HEOI 2016」求和
扫描右侧二维码阅读全文
31
2019/08

「TJOI / HEOI 2016」求和

题目链接:LOJ 2058

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

现在他想计算这样一个函数的值:

$$ f(n) = \sum_{i = 0} ^ {n} \sum_{j = 0} ^ {i} S(i, j) \cdot 2 ^ {j} \cdot j! $$

$S(i, j)$ 表示第二类斯特林数,递推公式为: $S(i, j) = j \cdot S(i − 1, j) + S(i − 1, j − 1), 1 \le j \le i − 1$。

边界条件为:$S(i, i) = 1(i \ge 0), S(i, 0) = 0(i \ge 1)$。

你能帮帮她吗?

数据范围:$1 \le n \le 10 ^ {5}$。


Solution

我们直接写出第二类斯特林数的通项公式:

$$ S(n, m) = \frac{1}{m!} \sum_{i = 0} ^ {m} (-1) ^ {i} \binom{m}{i} (m - i) ^ {n} $$

代入得到:

$$ \begin{align*} f(n) & = \sum_{i = 0} ^ {n} \sum_{j = 0} ^ {i} 2 ^ {j} \sum_{k = 0} ^ {j} (-1) ^ {k} \binom{j}{k} (j - k) ^ {i} \\ & = \sum_{i = 0} ^ {n} \sum_{j = 0} ^ {i} 2 ^ {j} \sum_{k = 0} ^ {j} (-1) ^ {k} \frac{j!}{k! \cdot (j - k)!} (j - k) ^ {i} \\ & = \sum_{i = 0} ^ {n} \sum_{j = 0} ^ {i} 2 ^ {j} \cdot j! \sum_{k = 0} ^ {j} \frac{(-1) ^ {k}}{k!} \frac{(j - k) ^ {i}}{(j - k)!} \\ & = \sum_{j = 0} ^ {n} 2 ^ {j} \cdot j! \sum_{k = 0} ^ {j} \frac{(-1) ^ {k}}{k!} \frac{\sum_{i = 0} ^ {n} (j - k) ^ {i}}{(j - k)!} \\ \end{align*} $$

设 $g(i) = \frac{(-1) ^ {i}}{i!}, h(i) = \frac{\sum_{j = 0} ^ n i ^ {j}}{i!} = \frac{i ^ {n +1} - 1}{(i - 1) \cdot i!}$。特殊地 $h(0) = 1, h(1) = n + 1$,那么答案为:

$$ \sum_{i = 0} ^ {n} 2 ^ {i} \cdot i! (g \ast h)(i) $$

直接做一遍卷积即可。

时间复杂度:$\mathcal O(n \log n)$。


Code

/* 此处省略多项式模板 */

const int N = 1e5;

int n, fac[N + 5], ifac[N + 5], pw[N + 5], in[N + 5];
std::vector<int> prime;
bool flg[N + 5];

void sieve(int n, int k) {
    std::fill(flg + 2, flg + n + 1, true);
    for (int i = 2; i <= n; i++) {
        if (flg[i]) prime.push_back(i), pw[i] = pow(i, k);
        for (auto j : prime) {
            if (i * j > n) break;
            flg[i * j] = false;
            pw[i * j] = 1LL * pw[i] * pw[j] % MOD;
            if (i % j == 0) continue;
        }
    }
    fac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = 1LL * fac[i - 1] * i % MOD;
    ifac[n] = inv(fac[n]);
    for (int i = n; i >= 1; i--) ifac[i - 1] = 1LL * ifac[i] * i % MOD;
    in[1] = 1;
    for (int i = 2; i <= n; i++) {
        in[i] = 1LL * (MOD - MOD / i) * in[MOD % i] % MOD;
    }
}
int main() {
    int n;
    scanf("%d", &n);
    sieve(n, n + 1);
    Vec F(n + 1), G(n + 1);
    for (int i = 0, sgn = 1; i <= n; i++, sgn = MOD - sgn) {
        F[i] = 1LL * sgn * ifac[i] % MOD;
    }
    G[0] = 1, G[1] = n + 1;
    for (int i = 2; i <= n; i++) {
        G[i] = 1LL * (pw[i] - 1) * in[i - 1] % MOD * ifac[i] % MOD;
    }
    F = F * G;
    int ans = 0;
    for (int p2 = 1, i = 0; i <= n; p2 = 2LL * p2 % MOD, i++) {
        add(ans, 1LL * p2 * fac[i] % MOD * F[i] % MOD);
    }
    printf("%d\n", ans);
    return 0;
}

发表评论