「解题报告」ARC154F Dice Game

看起来就多项式,跟概率有关就上概率生成函数吧。

考虑类似于 Flip Cells 的套路,设 \(F(x)\) 为翻出所有的生成函数,\(G(x)\) 为第一次翻出所有的生成函数,\(H(x)\) 是翻出后任意翻的生成函数,那么有 \(F(x)=G(x)H(x)\)

\(H(x)\) 显然等于 \(\frac{1}{1-x}\),即全翻出来之后可以随便翻,考虑 \(F(x)\)\(F(x)\) 可以看作是每一面至少翻出了 \(1\) 次,然后将操作序列合并起来。先写出 EGF 的形式,即 \((e^{\frac{x}{n}} - 1)^n\)。然后转成 OGF,即 \(\sum_{i=0}^n \binom{n}{i}(-1)^{n-i}\frac{1}{1-\frac{ix}{n}}\)。这个东西可以分治乘法求出 \(\frac{A(x)}{B(x)}\) 的形式。那么我们就有 \(G(x)=(1-x)F(x)\) 了。

然后考虑如何求 \(k\) 次方的贡献。有个结论,\([x^k]F(e^x)\) 就是 \(k\) 次方的期望。证明展开。

考虑怎么求 \(F(e^x)\),同样可以先转成 OGF,同样的分治乘法的办法求出来之后再转回去。然后就能求出答案了。

用这个式子求出来之后会发现分子分母都没有常数项,需要上下同时除以一个 \(x\)

人傻常数大,还需要卡常。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 600005, P = 998244353, G = 3;
int qpow(int a, int b) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = 1ll * ans * a % P;
        a = 1ll * a * a % P;
        b >>= 1;
    }
    return ans;
}
const int GI = qpow(G, P - 2);
int r[MAXN];
struct Polynomial {
    vector<int> a;
    int len;
    int& operator[](int b) { return a[b]; }
    Polynomial(int len = 0) : len(len) { a.resize(len + 1); }
    Polynomial(initializer_list<int> l) : len(l.size() - 1), a(l) {}
    Polynomial& set(int b) { len = b, a.resize(b + 1); return *this; }
    static Polynomial resize(Polynomial a, int s) { Polynomial b = a; return b.set(s); }
    void reverse() { std::reverse(a.begin(), a.end()); }
    static void calcRev(int limit) {
        for (int i = 1; i < limit; i++)
            r[i] = (r[i >> 1] >> 1) | ((i & 1) * limit >> 1);
    }
    void ntt(int limit, bool rev) {
        set(limit);
        for (int i = 0; i < limit; i++)
            if (i < r[i]) swap(a[i], a[r[i]]);
        for (int mid = 1; mid < limit; mid <<= 1) {
            int step = qpow(rev ? GI : G, (P - 1) / (mid << 1));
            for (int l = 0; l < limit; l += (mid << 1)) {
                int w = 1;
                for (int j = 0; j < mid; j++, w = 1ll * w * step % P) {
                    int x = a[l + j], y = 1ll * w * a[l + j + mid] % P;
                    a[l + j] = (x + y) % P, a[l + j + mid] = (1ll * P + x - y) % P;
                }
            }
        }
        int invn = qpow(limit, P - 2);
        if (rev) {
            for (int i = 0; i < limit; i++) 
                a[i] = 1ll * a[i] * invn % P;
        }
    }
    void print() { for (int i : a) printf("%d ", i); printf("\n"); }
    Polynomial operator*(Polynomial b) {
        Polynomial a = *this, c;
        int n = a.len + b.len;
        int limit = 1;
        while (limit <= n) limit <<= 1;
        c.set(limit);
        calcRev(limit);
        a.ntt(limit, 0), b.ntt(limit, 0);
        for (int i = 0; i <= limit; i++) c[i] = 1ll * a[i] * b[i] % P;
        c.ntt(limit, 1);
        c.set(n);
        return c;
    }
    Polynomial operator*(int b) {
        Polynomial c = *this;
        for (int& i : c.a) i = 1ll * i * b % P;
        return c;
    }
    Polynomial operator+(int b) {
        Polynomial c = *this;
        c[0] = (1ll * c[0] + b + P) % P;
        return c;
    }
    Polynomial operator+(Polynomial b) {
        Polynomial c = *this;
        c.set(max(c.len, b.len));
        for (int i = 0; i <= b.len; i++) c[i] = (c[i] + b[i]) % P;
        return c;
    }
    Polynomial operator-(Polynomial b) {
        Polynomial c = *this;
        c.set(max(c.len, b.len));
        for (int i = 0; i <= b.len; i++) c[i] = (c[i] - b[i] + P) % P;
        return c;
    }
    Polynomial inv(int n) {
        Polynomial b;
        b[0] = qpow(a[0], P - 2);
        for (int d = 2; d < (n << 1); d <<= 1) {
            Polynomial a = *this;
            a.set(d - 1);
            int limit = d << 1;
            calcRev(limit);
            a.ntt(limit, 0), b.ntt(limit, 0);
            for (int i = 0; i < limit; i++) b[i] = (2ll - 1ll * a[i] * b[i] % P + P) % P * b[i] % P;
            b.ntt(limit, 1);
            b.set(d - 1);
        }
        b.set(n - 1);
        return b;
    }
    Polynomial sqrt(int n) {
        static int TWOINV = qpow(2, P - 2);
        Polynomial b;
        b[0] = 1;
        for (int d = 2; d < (n << 1); d <<= 1) {
            Polynomial a = *this, c = b.inv(d);
            a.set(d - 1);
            int limit = d << 1;
            calcRev(limit);
            a.ntt(limit, 0), c.ntt(limit, 0);
            for (int i = 0; i < limit; i++) a[i] = 1ll * a[i] * c[i] % P;
            a.ntt(limit, 1);
            b.set(d - 1);
            for (int i = 0; i < d; i++) b[i] = 1ll * (a[i] + b[i]) * TWOINV % P;
        }
        b.set(n - 1);
        return b;
    }
    Polynomial derivative() {
        Polynomial b(len - 1);
        for (int i = 1; i <= len; i++) b[i - 1] = 1ll * a[i] * i % P;
        return b;
    }
    Polynomial integral() {
        Polynomial b(len + 1);
        for (int i = 0; i <= len; i++) b[i + 1] = 1ll * a[i] * qpow(i + 1, P - 2) % P;
        return b;
    }
    Polynomial ln(int n) {
        return (derivative() * inv(n)).integral().set(n - 1);
    }
    Polynomial exp(int n) {
        Polynomial b;
        b[0] = 1;
        for (int d = 1; d < (n << 1); d <<= 1) {
            Polynomial a = *this, e = b.ln(d);
            a.set(d - 1);
            b = b * (e * (P - 1) + a + 1);
            b.set(d - 1);
        }
        b.set(n - 1);
        return b;
    }
    Polynomial pow(int b, int n) {
        return (ln(n) * b).exp(n);
    }
    pair<Polynomial, Polynomial> operator/(Polynomial b) {
        int n = len, m = b.len;
        Polynomial ra = *this, rb = b, rc, d;
        ra.reverse(), rb.reverse();
        rc = ra * rb.inv(n - m + 1);
        rc.set(n - m);
        rc.reverse();
        d = *this - b * rc;
        d.set(m - 1);
        return make_pair(rc, d);
    }
} a;
int n, m;
int fac[MAXN], inv[MAXN];
void pre(int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; i++) {
        fac[i] = 1ll * fac[i - 1] * i % P;
    }
    inv[n] = qpow(fac[n], P - 2);
    for (int i = n; i >= 1; i--) {
        inv[i - 1] = 1ll * inv[i] * i % P;
    }
}
int C(int n, int m) {
    if (n < 0 || m < 0 || n < m) return 0;
    return 1ll * fac[n] * inv[m] % P * inv[n - m] % P;
}
pair<Polynomial, Polynomial> solve(int l, int r) {
    if (l == r) {
        if (l == 0) {
            return { 
                Polynomial{ (int) (C(n, l) * (((n - l) & 1) ? P - 1ll : 1ll) % P) }, 
                Polynomial{ 1 } 
            };
        }
        return { 
            Polynomial{ (int) (C(n, l) * (((n - l) & 1) ? P - 1ll : 1ll) % P) }, 
            Polynomial{ 1, (int) ((P - 1ll) * l % P * qpow(n, P - 2) % P) } 
        };
    }
    int mid = (l + r) >> 1;
    auto a = solve(l, mid), b = solve(mid + 1, r);
    if (a.first.len == 0 && a.first[0] == 0) return b;
    return { a.first * b.second + a.second * b.first, a.second * b.second };
}
pair<Polynomial, Polynomial> solve2(Polynomial &p, int l, int r) {
    if (l == r) {
        if (l == 0) {
            return { Polynomial{ p[l] }, Polynomial{ 1 } };
        }
        return { 
            Polynomial{ p[l] }, 
            Polynomial{ 1, P - l } 
        };
    }
    int mid = (l + r) >> 1;
    auto a = solve2(p, l, mid), b = solve2(p, mid + 1, r);
    if (a.first.len == 0 && a.first[0] == 0) return b;
    return { a.first * b.second + a.second * b.first, a.second * b.second };
}
Polynomial solve3(Polynomial f) {
    auto g = solve2(f, 0, f.len);
    // g.first.print();
    // g.second.print();
    auto h = (g.first * g.second.inv(m + 1)).set(m + 1);
    for (int i = 0; i <= h.len; i++) {
        h[i] = 1ll * h[i] * inv[i] % P;
    }
    return h;
}
int main() {
    scanf("%d%d", &n, &m);
    pre(n + m);
    auto f = solve(0, n);
    f.first = solve3(f.first * Polynomial{ 1, P - 1 });
    f.second = solve3(f.second);
    auto a = Polynomial(f.first.len - 1);
    for (int i = 1; i <= f.first.len; i++)
        a[i - 1] = f.first[i];
    auto b = Polynomial(f.second.len - 1);
    for (int i = 1; i <= f.second.len; i++)
        b[i - 1] = f.second[i];
    auto g = (a * b.inv(m + 1)).set(m);
    for (int i = 0; i <= g.len; i++) {
        g[i] = 1ll * g[i] * fac[i] % P;
    }
    for (int i = 1; i <= m; i++) {
        printf("%d\n", g[i]);
    }
    return 0;
}
posted @ 2023-03-15 16:37  APJifengc  阅读(43)  评论(2编辑  收藏  举报