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;
}
}