P5405 [CTS2019] 氪金手游 题解
首先需要发现的是题目给出的条件等价于是限制所有卡形成了一棵树,但树边的方向是不确定的。从其它地方不好入手,不妨先考虑这棵树边全都从父亲指向儿子的情形,换句话说就是根节点要比所有子树内的节点先取到。
考虑一个点比子树内的节点都先取到的概率:我们记 \(S\) 表示全局 \(w\) 的和,\(S_x\) 表示 \(S\) 子树内 \(w\) 的和,那么考虑实际上就是让 \(x\) 之前被取到的都是其子树外的,考虑枚举 \(x\) 是第几个被取到的,那么实际上就是:
\[\frac{w_x}{S}\sum_{i=0}^{+\infty}(\frac{S-S_x}S)^i
\]
稍稍化简一下可以得到就是 \(\dfrac{w_x}{S_W}\)。
得到这个式子之后发现可以直接树形 dp 来做。设 \(f_{i,j}\) 表示在 \(i\) 子树中,子树 \(w\) 和为 \(j\) 的概率,直接转移就是 \(O(n^2)\) 的。
然后考虑如何处理反向边。直接处理是困难的,考虑容斥,用不考虑这条边的概率减去考虑这条边为正向的概率,实际上就是利用正反概率之和为 1,等价于不考虑边的性质巧妙处理。那么转移的时候分别增加 +1 和 -1 的系数即可。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1005, mod = 998244353;
void add(int &x, int y) {x = (x + y) % mod;
}
void del(int &x, int y) {x = (x - y + mod) % mod;
}
int qpow(int x, int y) {int ans = 1;while (y) {if (y & 1) ans = ans * x % mod;x = x * x % mod;y >>= 1; }return ans;
}
struct Node {int to, nxt, id;
} e[N << 1];
int head[N], cnt;
void add(int u, int v, int id) {e[++cnt] = {v, head[u], id};head[u] = cnt;
}int n, inv[N * 3];
int a[N][3];
int dp[N][N * 3], siz[N];
int tmp[N * 3];void dfs(int x, int fa) {int s = a[x][0] + a[x][1] + a[x][2];s = qpow(s, mod - 2) % mod;dp[x][1] = a[x][0] * s % mod;dp[x][2] = a[x][1] * s * 2 % mod;dp[x][3] = a[x][2] * s * 3 % mod;siz[x] = 1;for (int w = head[x]; w; w = e[w].nxt) {int y = e[w].to;if (y == fa) continue;dfs(y, x);for (int i = 1; i <= siz[x] * 3; i++)for (int j = 1; j <= siz[y] * 3; j++) {int t = dp[x][i] * dp[y][j] % mod;if (!e[w].id) del(tmp[i + j], t), add(tmp[i], t);else add(tmp[i + j], t);}siz[x] += siz[y];for (int i = 1; i <= siz[x] * 3; i++) dp[x][i] = tmp[i], tmp[i] = 0;}for (int i = 1; i <= siz[x] * 3; i++) dp[x][i] = dp[x][i] * inv[i] % mod;
}signed main() {inv[1] = 1;for (int i = 2; i < N * 3; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;ios::sync_with_stdio(0);cin.tie(0);cin >> n;for (int i = 1; i <= n; i++) cin >> a[i][0] >> a[i][1] >> a[i][2];for (int i = 1; i < n; i++) {int x, y;cin >> x >> y;add(x, y, 1);add(y, x, 0);}dfs(1, 0);int ans = 0;for (int i = 1; i <= siz[1] * 3; i++) add(ans, dp[1][i]);cout << ans << '\n';return 0;
}
