洛谷P5405 [CTS2019]氪金手游(期望,容斥)
题目大意
小刘同学是一个喜欢氪金手游的男孩子。
他最近迷上了一个新游戏,游戏的内容就是不断地抽卡。现在已知:
-
卡池里总共有 (N) 种卡,第 (i) 种卡有一个权值 (W_i),小刘同学不知道 (W_i) 具体的值是什么。但是他通过和网友交流,他了解到 (W_i) 服从一个分布。
-
具体地,对每个 (i),小刘了解到三个参数 (p_{i,1},p_{i,2},p_{i,3}),(W_i) 将会以 (p_{i,j}) 的概率取值为 (j),保证 (p_{i,1}+p_{i,2}+p_{i,3}=1)。
小刘开始玩游戏了,他每次会氪一元钱来抽一张卡,其中抽到卡 (i) 的概率为:
小刘会不停地抽卡,直到他手里集齐了全部 (N) 种卡。
抽卡结束之后,服务器记录下来了小刘第一次得到每张卡的时间 (T_i)。游戏公司在这里设置了一个彩蛋:公司准备了 (N−1) 个二元组 ((u_i,v_i)),如果对任意的 (i),成立 (T_{u_i}<T_{v_i}),那么游戏公司就会认为小刘是极其幸运的,从而送给他一个橱柜的手办作为幸运大奖。
游戏公司为了降低获奖概率,它准备的这些 ((u_i,v_i)) 满足这样一个性质:对于任意的 (varnothing
e Ssubsetneq{1,2,ldots,N}),总能找到 ((u_i,v_i)) 满足:(u_iin S,v_i
otin S) 或者 (u_i
otin S,v_iin S)。
请你求出小刘同学能够得到幸运大奖的概率,可以保证结果是一个有理数,请输出它对 (998244353) 取模的结果。
数据范围
对于全部的测试数据,保证 (Nle 1000),(a_{i,j}le 10^6)。
- (20) 分的数据,(Nle 15)。
- (15) 分的数据,(Nle 200),且每个限制保证 (|u_i−v_i|=1)。
- (20) 分的数据,(Nle 1000),且每个限制保证 (|u_i−v_i|=1)。
- (15) 分的数据,(Nle 200)。
- (30) 分的数据,无特殊限制。
解题思路
和 LOJ 不等关系很像
首先考虑最简单的情况,一条外向链的所有 w 都确定的情况下答案是多少
显然有 (large Ans = prodfrac{w_i}{sum_{j=i}^nw_j})
如果 w 不确定,那么我们直接 dp 一维记录 w 即可,因为 w 很小
那么现在除了外向边还增加了内向边,直接容斥即可,在 dp 的时候同时将容斥系数带进去即可
跑个树形背包就完了
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x, char ed = '
')
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar(ed);
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int P = 998244353;
const int N = 1055;
int h[N], ne[N<<1], to[N<<1], tot;
inline void add(int x, int y) {
ne[++tot] = h[x], to[h[x] = tot] = y;
}
ll f[N][3*N], inv[N], n;
ll fpw(ll x, ll mi) {
ll res = 1;
for (; mi; mi >>= 1, x = x * x % P)
if (mi & 1) res = res * x % P;
return res;
}
int siz[N];
void dfs(int x, int fa) {
siz[x] = 3; ll g[3 * N];
for (int i = h[x]; i; i = ne[i]) {
int y = to[i]; if (y == fa) continue;
dfs(y, x); memset(g, 0, (siz[x] + siz[y]) * 8 + 200);
for (int j = 1;j <= siz[y]; j++) {
for (int k = 1;k <= siz[x]; k++) {
ll t = f[x][k] * f[y][j] % P;
if (i & 1) g[j + k] = (g[j + k] + t) % P;
else g[k] = (g[k] + t) % P, g[j + k] = (g[j + k] + P - t) % P;
}
}
siz[x] += siz[y], memcpy(f[x], g, siz[x] * 8 + 200);
}
for (int i = 1;i <= siz[x]; i++) f[x][i] = f[x][i] * inv[i] % P;
}
int main() {
read(n);
for (int i = 1;i <= n; i++) {
ll v1, v2, v3, t;
read(v1), read(v2), read(v3);
t = fpw((v1 + v2 + v3) % P, P - 2);
f[i][1] = v1 * t % P;
f[i][2] = 2 * v2 * t % P;
f[i][3] = 3 * v3 * t % P;
}
for (int i = 1, x, y;i < n; i++)
read(x), read(y), add(x, y), add(y, x);
inv[0] = inv[1] = 1;
for (int i = 2;i <= 3 * n; i++)
inv[i] = inv[P % i] * (P - P / i) % P;
dfs(1, 0); ll ans = 0;
for (int i = 1;i <= siz[1]; i++) ans += f[1][i];
write(ans % P);
return 0;
}