题解 P5405 【[CTS2019]氪金手游】
我支持香港警察,你们可以来轰炸我博客了
Problem
题意概要:给定
在确定了所有的
给定
问满足所有二元组限制的概率。
Solution
(话说考场上看错了题,以为是确定好每个点抽中的概率后再进行游戏,打了正解加仨暴力,跑出来都是一样的就是和大样例不一样,导致心态崩了 而且题面里并没有提到这个区别)
这题的解题突破口在于这个题的限制是一棵树,但边的方向不唯一(没法找到根使得这棵树变成内向树或外向树)
为了解题方便,不妨设这棵树是以
那么可以发现,
设
sz_i 表示i 的子树中概率系数的和(\sum_{v\in subtree(x)}w_v )
即设
那么可以用一个Dp解决这个问题:设
毒瘤出题人居然不给这档部分分?
考虑到这棵树不是外向树,存在若干条内向边。
直接处理内向边不好处理,现在存在一种方法,即用 “不考虑这条边”的方案数 减去 “考虑这条边为外向边”的方案数
(用图来表达即:
然后在树上Dp时,遇到此类内向边就将权值用上面这种方法计算一下即可。
听说这题还有另一种做法,代码实现上是一样的,但思想不大一样:
考虑设
而 “至少
这样在Dp的时候加入一个容斥系数即可:一开始设所有内向边断开,然后给其加上一个负的 “内向边变外向边” 的权值
Code
//loj-3124
#include <cstdio>
typedef long long ll;
const int N = 1013, p = 998244353;
struct Edge {int v, w, nxt;} a[N<<1];
int head[N], sz[N];
int a1[N], a2[N], a3[N];
int F[N][N*3], arr[N*3];
int inv[N*3], n, _;
inline int qpow(int A, int B) {
int res = 1; while(B) {
if(B&1) res = (ll)res * A%p;
A = (ll)A * A%p, B >>= 1;
} return res;
}
void dfs(int x, int las) {
int*f = F[x];
f[0] = 1;
for(int ii=head[x],v;ii;ii=a[ii].nxt)
if((v=a[ii].v) != las) {
dfs(v, x);
int*g = F[v];
const int sx = sz[x] * 3, sv = sz[v] * 3;
for(int i=0;i<=sx+sv;++i) arr[i] = 0;
if(!a[ii].w)
for(int i=0;i<=sx;++i)
for(int j=0;j<=sv;++j)
arr[i+j] = (arr[i+j] + (ll)f[i] * g[j])%p;
else {
ll sm = 0;
for(int i=0;i<=sv;++i) sm += g[i];
sm %= p;
for(int i=0;i<=sx;++i) arr[i] = sm * f[i]%p;
for(int i=0;i<=sx;++i)
for(int j=0;j<=sv;++j)
arr[i+j] = (arr[i+j] - (ll)f[i] * g[j] + (ll)p*p)%p;
}
for(int i=0;i<=sx+sv;++i) f[i] = arr[i];
sz[x] += sz[v];
}
const int sx = sz[x] * 3;
for(int i=0;i<=sx+3;++i) arr[i] = 0;
for(int i=0;i<=sx;++i) {
arr[i+1] = (arr[i+1] + (ll)f[i] * a1[x]%p * inv[i+1])%p;
arr[i+2] = (arr[i+2] + (ll)f[i] * a2[x]%p * inv[i+2] * 2)%p;
arr[i+3] = (arr[i+3] + (ll)f[i] * a3[x]%p * inv[i+3] * 3)%p;
}
for(int i=0;i<=sx+3;++i) f[i] = arr[i];
++sz[x];
}
int main() {
scanf("%d",&n);
inv[0] = inv[1] = 1;
for(int i=2;i<=n*3;++i) inv[i] = (ll)(p-p/i) * inv[p%i]%p;
for(int i=1;i<=n;++i) {
scanf("%d%d%d",a1+i,a2+i,a3+i);
ll v = qpow(a1[i] + a2[i] + a3[i], p-2);
a1[i] = v * a1[i]%p;
a2[i] = v * a2[i]%p;
a3[i] = v * a3[i]%p;
}
for(int i=1,x,y;i<n;++i) {
scanf("%d%d",&x,&y);
a[++_].v = y, a[_].w = 0, a[_].nxt = head[x], head[x] = _;
a[++_].v = x, a[_].w = 1, a[_].nxt = head[y], head[y] = _;
}
dfs(1, 0);
ll Ans = 0;
for(int i=sz[1]*3;~i;--i)
Ans += F[1][i];
printf("%lld\n", Ans%p);
return 0;
}