21牛客多校10 - Game of Death(思维,概率)

题目

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

题解

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

可得

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

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

\[f(i)=\sum\limits_{j\le i}{(-1)^{i-j}\cdot C(i,j) \cdot g(j)} \]

可以直接卷积。

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

#include <bits/stdc++.h>

#define endl '\n'
#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;
    }
}
上一篇:生产者MessageQueueSelector实战


下一篇:设计模式-工厂模式