zoukankan      html  css  js  c++  java
  • 21牛客多校10

    题目

    G-Game of Death_2021牛客暑期多校训练营10

    题解

    (f(S))代表集合(S)的人均被击中概率,(g(S))代表击中的人是(S)的子集的概率。

    可得

    [f(S)=sumlimits_{T subseteq S}{(-1)^{|S|-|T|}cdot g(T)} ]

    由于所有相同大小的集合本质是一样的,所有相同大小的集合的(f)(g)概率均相同。所以可以将子集看作子集大小直接算,然后推式子即可。(g式子推导略)

    [f(i)=sumlimits_{jle i}{(-1)^{i-j}cdot C(i,j) cdot g(j)} ]

    可以直接卷积。

    关键在于(f)(g)的定义。

    #include <bits/stdc++.h>
    
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    
    const int N = 2e6 + 10;
    const int M = 998244353;
    const double eps = 1e-5;
    
    inline ll qpow(ll a, ll b, ll m) {
        ll res = 1;
        while(b) {
            if(b & 1) res = (res * a) % m;
            a = (a * a) % m;
            b = b >> 1;
        }
        return res;
    }
    
    int rev[N];
    
    void change(ll y[], int len) {
        for (int i = 0; i < len; ++i) {
            rev[i] = rev[i >> 1] >> 1;
            if (i & 1) {
                rev[i] |= len >> 1;
            }
        }
        for (int i = 0; i < len; ++i) {
            if (i < rev[i]) {
                swap(y[i], y[rev[i]]);
            }
        }
        return;
    }
    
    void fft(ll y[], int len, int on) {
        change(y, len);
        for (int h = 2; h <= len; h <<= 1) {
            ll gn = qpow(3, (M - 1) / h, M);
            if (on == -1)
                gn = qpow(gn, M - 2, M);
            for (int j = 0; j < len; j += h) {
                ll g = 1;
                for (int k = j; k < j + h / 2; k++) {
                    ll u = y[k];
                    ll t = g * y[k + h / 2] % M;
                    y[k] = (u + t) % M;
                    y[k + h / 2] = (u - t + M) % M;
                    g = g * gn % M;
                }
            }
        }
        if (on == -1) {
            ll inv = qpow(len, M - 2, M);
            for (int i = 0; i < len; i++) {
                y[i] = y[i] * inv % M;
            }
        }
    }
    
    int get(int x) {
        int res = 1;
        while (res < x) {
            res <<= 1;
        }
        return res;
    }
    
    ll p, rn;
    int n, a, b;
    
    ll G(ll x) {
        ll a = ((x - 1) + (n - x) * (1 - p) % M) * rn % M;
        ll b = (x + (n - x - 1) * (1 - p) % M) * rn % M;
        return qpow(a + M, x, M) * qpow(b + M, n - x, M) % M;
    }
    
    ll f[N], g[N];
    
    
    ll fact[N], rfact[N];
    ll C(int n, int m) {
        if(n < m) return 0;
        return fact[n] * rfact[n - m] % M * rfact[m] % M;
    }
    
    
    
    int main() {
        IOS;
        fact[0] = rfact[0] = 1;
        for(int i = 1; i < N; i++) {
            fact[i] = fact[i - 1] * i % M;
            rfact[i] = qpow(fact[i], M - 2, M);
        }
        cin >> n >> a >> b;
        p = a * qpow(b, M - 2, M) % M;
        rn = qpow(n - 1, M - 2, M);
    
        for(int i = 0; i <= n; i++) g[i] = G(i) * rfact[i] % M;
        for(int i = 0; i <= n; i++) f[i] = ((i % 2 ? -1 : 1) * rfact[i] % M + M) % M;
    
        int len = get(2 * (n + 1) + 1);
        fft(f, len, 1);
        fft(g, len, 1);
        for(int i = 0; i < len; i++) f[i] = f[i] * g[i] % M;
        fft(f, len, -1);
    
        for(int i = 0; i <= n; i++) {
            cout << fact[n - i] * C(n, n - i) % M * f[n - i] % M << endl;
        }
    }
    
  • 相关阅读:
    《你不知道的javascript》读书笔记2
    你不知道的console调试
    《你不知道的javascript》读书笔记1
    使用js做LeetCode
    用装饰器来进行登录验证
    python 解压序列
    pycharm 的live_template的使用
    faker 库的使用
    Python常用内置类和常用内置函数汇总
    迭代器 ,生成器(生成器函数/生成器表达式)
  • 原文地址:https://www.cnblogs.com/limil/p/15171965.html
Copyright © 2011-2022 走看看