Universal OJ#450. 「集训队作业2018」复读机 – 题解

主要思路

首先对于 \(d = 1\),答案就是 \(k^n\)。

对于 \(d = 2\),其实发现可以用生成函数来直接乘:\( A(x) = \sum_{i = 0}^\infty \frac{1}{i!} x^i [2 | i] \),那么答案就是 \( A^k(x)[x^n] \)。发现可以变通奇偶性,所以 \(A(x) = \frac{e^x + e^{-x}}{2}\),然后试着二项式展开:

\[ \begin{aligned} A^k(x) &= (\frac{e^x + e^{-x}}{2})^k = \frac{1}{2^k} (e^x + e^{-x})^k \\ &= \frac{1}{2^k} \sum_{i = 0}^k {k \choose i} e^{ix} e^{-(k – i)x} \\ &= \frac{1}{2^k} \sum_{i = 0}^k {k \choose i} e^{(2i – k)x} \end{aligned} \]

则第 \(n\) 项就是 \(\frac{1}{2^k} \sum_{i = 0}^k {k \choose i} (2i – k)^n \)。这个还是很好算的。

对于 \(d = 3\) 时,跟原来的式子一样,但是发现没法用奇偶性搞,所以可以考虑用单位根反演:

\[ \frac{1}{d} \sum_{i = 0}^{d – 1} (\omega_d^k)^i = [d | k] \\ \begin{aligned} A(x) &= \sum_{i = 0}^\infty \frac{1}{i!} x^i \frac{1}{d} \sum_{j = 0}^{d – 1} (\omega_d^i)^j \\ &= \frac{1}{d} \sum_{i = 0}^{d – 1} \omega_d^i e^{\omega_d^1 x} \end{aligned} \]

因为此时 \(k\) 比较小,所以可以考虑直接暴力二项式展开即可。

代码

// UOJ450.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 501000, mod = 19491001, g = 7;

int n, k, d, fac[MAX_N], fac_inv[MAX_N], inv[MAX_N], omega[3];

int fpow(int bas, int tim)
{
    int ret = 1;
    while (tim)
    {
        if (tim & 1)
            ret = 1LL * ret * bas % mod;
        bas = 1LL * bas * bas % mod;
        tim >>= 1;
    }
    return ret;
}

const int inv2 = fpow(2, mod - 2);

int binomial(int n_, int k_) { return 1LL * fac[n_] * fac_inv[k_] % mod * fac_inv[n_ - k_] % mod; }

int main()
{
    scanf("%d%d%d", &n, &k, &d);
    if (d == 1)
        printf("%d\n", fpow(k, n)), exit(0);
    inv[0] = inv[1] = 1;
    for (int i = 2; i <= k; i++)
        inv[i] = 1LL * inv[mod % i] * (mod - mod / i) % mod;
    for (int i = fac[0] = fac_inv[0] = 1; i <= k; i++)
        fac[i] = 1LL * fac[i - 1] * i % mod, fac_inv[i] = 1LL * fac_inv[i - 1] * inv[i] % mod;
    if (d == 2)
    {
        int ans = 0;
        for (int j = 0; j <= k; j++)
            ans = (1LL * binomial(k, j) * fpow((2LL * j + mod - k) % mod, n) % mod + ans) % mod;
        ans = 1LL * ans * fpow(inv2, k) % mod;
        printf("%d\n", ans);
    }
    else
    {
        int ans = 0;
        omega[0] = 1, omega[1] = fpow(g, (mod - 1) / 3), omega[2] = 1LL * omega[1] * omega[1] % mod;
        for (int i = 0; i <= k; i++)
            for (int j = 0; i + j <= k; j++)
            {
                int p = (0LL + (k - i - j) + 1LL * omega[1] * i % mod + 1LL * omega[2] * j % mod) % mod;
                ans = (0LL + ans + 1LL * binomial(k, i) * binomial(k - i, j) % mod * fpow(p, n) % mod) % mod;
            }
        printf("%lld\n", 1LL * ans * fpow(fpow(d, k), mod - 2) % mod);
    }
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *