zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    SQL Cookbook:二、查询结果排序(4)对字母数字混合的数据排序
    (转).net框架读书笔记引用参数(ref/out)
    (转).net面试问答(大汇总)
    SQL Cookbook:一、检索记录(9)限制返回的行数
    C# 3.0 Cookbook:一、字符与字符串处理(5):把一个字符串与另一个字符串的头部或尾部作比较
    (转)sql海量数据优化
    (转)C# 中的委托和事件(二)
    (转)我看微软.NET各技术应用前景
    在sql server数据库中快速删除记录,清空表
    SQL Cookbook:二、查询结果排序(3)按子串排序
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/10281936.html
Copyright © 2011-2022 走看看