Luogu 4705 玩游戏

Posted czxingchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu 4705 玩游戏相关的知识,希望对你有一定的参考价值。

看见这个题依稀想起了$5$月月赛时候的事情,到现在仍然它感觉非常神仙。

游戏$k$次价值的期望答案

$$ans_k = frac{1}{nm}sum_{i = 1}^{n}sum_{j = 1}^{n}(a_i + b_j)^k$$

二项式定理展开

$$ans_k=frac{1}{nm}sum_{i = 1}^{n}sum_{j = 1}^{m}sum_{t = 0}^{k}inom{k}{t}a_i^tb_j^{k - t}$$

$$ = frac{1}{nm}sum_{t = 0}^{k}inom{k}{t}sum_{i = 1}^{n}a_i^tsum_{j = 1}^{m}b_j^{k - t}$$

$$= frac{k!}{nm}frac{sum_{i = 1}^{n}a_i^t}{t!}frac{sum_{j = 1}^{m}b_j^{k - t}}{(k - t)!}$$

容易发现这后面是一个卷积的形式。

我们设$A(x) = frac{sum_{i = 1}^{n}a_i^x}{x!}$,$B(x) = frac{sum_{i = 1}^{n}b_i^x}{x!}$,答案就变成了

$$frac{k!}{nm}(A * B)(k)$$

现在考虑怎么算这个$sum_{i = 1}^{n}a_i^k$。

不会了,点开题解

我们构造一个多项式$F(x) = prod_{i = 1}^{n}(1 - a_ix)$,这个$F(x)$可以通过分治算出来,时间复杂度$T(n) = 2T(frac{n}{2}) + O(nlogn)$趋向$O(nlogn)$???。

接下来要开始变魔术了,

设$G(x) = lnF(x)$,

$$G(x) = sum_{i = 1}^{n}ln(1 - a_ix)$$

对里面的$ln$在$x_0 = 0$处泰勒展开。

我们知道

$$ln(1 - x) = 0 - frac{x}{1} - frac{x^2}{2} - frac{x^3}{3} - cdots = -sum_{i = 1}^{infty}frac{x^i}{i}$$

所以

$$G(x) = sum_{i = 1}^{n}sum_{j = 1}^{k}-frac{a_i^j}{j}x^j$$

因为这个题对$x^{k + 1}$次取模了,所以后面的项可以不用再写了。

变形一下

$$G(x) = sum_{j = 1}^{k}(-frac{1}{j}sum_{i = 1}^{n}a_i^j)x^j$$

发现$1$到$k$的$sum_{i = 1}^{n}a_i^k$直接算出来了,而$i = 0$时候这东西显然为$n$。

鼓掌~~~

现在回过头来考虑一下这个魔术是怎么变出来的。

考虑构造每一个$a_i$的生成函数

$$1 + a_ix + a_i^2x^2 + a_i^3x^3 + cdots$$

把每一个$a_i$的生成函数都构造出来然后加起来的第$i$项系数就是$k = i$时候的答案了。

$$F(x) = sum_{i = 1}^{n}frac{1}{1 - a_ix}$$

发现这个$F(x)$并不是很好算,而

$$ln‘(1 - a_ix) = frac{1}{1 - a_ix}$$

$$(ln(1 - a_ix))‘ = frac{-a_i}{1 - a_ix}$$

所以先计算

$$G(x) = sum_{i = 1}^{n}frac{-a_i}{1 - a_ix} = (lnprod_{i = 1}^{n}(1 - a_ix))‘$$

把这两个式子写回到生成函数的形式,发现

$$F(x) = -x * G(x) + n$$

于是大功告成。

用$vector$写多项式真爽。

技术分享图片
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
typedef vector <ll> poly;

const int N = 1e5 + 5;
const int Maxn = 1e5;

int n, m, K;
ll a[N], b[N], fac[N], ifac[N], inv[N];

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for (; ch > 9 || ch < 0; ch = getchar())
        if (ch == -) op = -1;
    for (; ch >= 0 && ch <= 9; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

inline void deb(poly c) {
    for (int i = 0; i < (int)c.size(); i++)
        printf("%lld%c", c[i], " 
"[i == (int)c.size() - 1]);
}

namespace Poly {
    const int L = 1 << 20;
    const ll P = 998244353LL;

    int lim, pos[L];

    inline ll fpow(ll x, ll y) {
        ll res = 1;
        for (; y > 0; y >>= 1) {
            if (y & 1) res = res * x % P;
            x = x * x % P;
        }
        return res;
    }

    template <typename T>
    inline void inc(T &x, T y) {
        x += y;
        if (x >= P) x -= P;
    }

    template <typename T>
    inline void sub(T &x, T y) {
        x -= y;
        if (x < 0) x += P;
    }

    inline void prework(int len) {
        int l = 0;
        for (lim = 1; lim < len; lim <<= 1, ++l);
        for (int i = 0; i < lim; i++)
            pos[i] = (pos[i >> 1] >> 1) | ((i & 1) << (l - 1));
    }

    inline void ntt(poly &c, int opt) {
        c.resize(lim, 0);
        for (int i = 0; i < lim; i++)
            if (i < pos[i]) swap(c[i], c[pos[i]]);
        for (int i = 1; i < lim; i <<= 1) {
            ll wn = fpow(3, (P - 1) / (i << 1));
            if (opt == -1) wn = fpow(wn, P - 2);
            for (int len = i << 1, j = 0; j < lim; j += len) {
                ll w = 1;
                for (int k = 0; k < i; k++, w = w * wn % P) {
                    ll x = c[j + k], y = w * c[j + k + i] % P;
                    c[j + k] = (x + y) % P, c[j + k + i] = (x - y + P) % P;
                }
            }
        }

        if (opt == -1) {
            ll inv = fpow(lim, P - 2);
            for (int i = 0; i < lim; i++) c[i] = c[i] * inv % P;
        }
    }

    inline poly operator * (const poly &x, const poly &y) {
        poly res, u = x, v = y;
        prework(u.size() + v.size() - 1);
        ntt(u, 1), ntt(v, 1);
        for (int i = 0; i < lim; i++) res.push_back(v[i] * u[i] % P);
        ntt(res, -1);
        res.resize(u.size() + v.size() - 1);
        return res;
    }

    poly getInv(poly x, int len) {
        x.resize(len);
        if (len == 1) {
            poly res;
            res.push_back(x[0]);
            return res;
        }
        poly y = getInv(x, (len + 1) >> 1);
        prework(len << 1);

        poly u = x, v = y, res;
        ntt(u, 1), ntt(v, 1);
        for (int i = 0; i < lim; i++) res.push_back(v[i] * (2LL - u[i] * v[i] % P + P) % P);
        ntt(res, -1);

        res.resize(len);
        return res;
    }

    inline void direv(poly &c) {
        for (int i = 0; i < (int)c.size() - 1; i++)
            c[i] = c[i + 1] * (i + 1) % P;
        c[c.size() - 1] = 0;
    }

    inline void integ(poly &c) {
        for (int i = (int)c.size() - 1; i > 0; i--)
            c[i] = c[i - 1] * inv[i] % P;
        c[0] = 0;
    }

    inline poly getLn(poly c) {
        poly a = getInv(c, (int)c.size());
        poly b = c;
        direv(b);

        poly res = b * a;
        res.resize(c.size());
        integ(res);
        return res;
    }

    inline poly solve(int l, int r, ll *c) {
        if (l == r) {
            poly res;
            res.push_back(1), res.push_back((P - c[l]) % P);
            return res;
        }

        int mid = ((l + r) >> 1);
        poly u = solve(l, mid, c), v = solve(mid + 1, r, c);
        poly res = u * v;
        res.resize(u.size() + v.size() - 1);
        return res;
    }

    inline poly mul(const poly &x, const poly &y) {
        return x * y;
    }

}

using Poly::P;
using Poly::fpow;
using Poly::inc;
using Poly::sub;
using Poly::solve;
using Poly::getLn;

inline void prework() {
    fac[0] = inv[0] = inv[1] = 1;
    for (int i = 1; i <= Maxn; i++) {
        fac[i] = fac[i - 1] * i % P;
        if (i > 1) inv[i] = (P - P / i) * inv[P % i] % P;
    }
    ifac[Maxn] = fpow(fac[Maxn], P - 2);
    for (int i = Maxn - 1; i >= 0; i--) ifac[i] = ifac[i + 1] * (i + 1) % P;
}

int main() {
/*    #ifndef ONLINE_JUDGE
        freopen("Sample.txt", "r", stdin);
    #endif    */
    
    freopen("input.txt", "r", stdin);
    freopen("my.out", "w", stdout);

    prework();

    read(n), read(m);
    for (int i = 1; i <= n; i++) read(a[i]);
    for (int i = 1; i <= m; i++) read(b[i]);
    read(K);
    ++K;

    poly Fa = solve(1, n, a), Fb = solve(1, m, b);

//    deb(Fa), deb(Fb);

    Fa.resize(K, 0), Fb.resize(K, 0);
    poly Ga = getLn(Fa), Gb = getLn(Fb);

    Ga.resize(K, 0), Gb.resize(K, 0);

//    deb(Ga), deb(Gb);

    poly A, B, C;

    A.push_back(n), B.push_back(m);
    for (int i = 1; i < K; i++) {
        A.push_back((P - Ga[i]) % P * i % P * ifac[i] % P);
        B.push_back((P - Gb[i]) % P * i % P * ifac[i] % P);
    }

//    deb(A), deb(B);

    C = Poly::mul(A, B);
    C.resize(K);

//    deb(C);

//    printf("
");
    ll invn = fpow(n, P - 2), invm = fpow(m, P - 2);
    for (int i = 1; i < K; i++)
        printf("%lld
", invn * invm % P * fac[i] % P * C[i] % P);

    return 0;
}
View Code

 

以上是关于Luogu 4705 玩游戏的主要内容,如果未能解决你的问题,请参考以下文章

luoguP4705 玩游戏 多项式运算+NTT

从片段调用 Google Play 游戏服务

Luogu P1199 三国游戏

Luogu2578 [ZJOI2005]九数码游戏

Luogu P3150 pb的游戏

「游戏引擎 浅入浅出」4.3 片段着色器