这应该是CTS2019最简单的题。----lhm
首先假设题目给的是一个单向的链,并且所有 (W) 都是确定的,那么显然第一步一定要选择链的起点,概率为 (frac{W_1}{S_{1...n}});第二步如果选到起点,就再选一次,因此相当于与起点无关,因此合法概率为 (frac{W_2}{S_{2...n}});第三步合法概率为(frac{W_3}{S_{3...n}})...
如果给的是一个外向树,我们发现儿子与儿子之间是互不影响的(显然),那么 (cur) 子树的合法概率是:(sum_{w[...]}frac{W_{cur}}{Sum[cur]}prod_{son}f_{son}),注意这里的(f_{son}) 是在 (son) 的 (W) 为 (w[son]) 的条件下的合法概率。
现在我们考虑不同概率的 (W)。发现子树内具体的 (W) 对父亲的答案没有影响,只有子树的和以及合法概率对答案有影响。因此我们考虑将子树 (W) 和计入状态。我们设 (f[cur][w]) 表示 (cur) 及其子树的 (W) 和为 (w) 的合法概率,然后可以树形背包解决。于是我们有:
(初始化,算入 (W_{cur}) 并设置 (w))
[f[cur][1/2/3]= (1/2/3) * a[1/2/3]
]
(加入一个子树)
[f[cur][i+j]<-f[cur][i] * f[to][j]
]
(每个 (cur) 结束后的操作,意义见上面的式子)
[f[cur][i] <- f[cur][i] / i
]
现在考虑如果有由儿子指向父亲的边该怎么办。正难则反,既然子树之间无关系,那么我们可以求出忽视这条边的限制(直接“删掉”边“分离”出子树)以及这条边是正向边(同上)的“合法”概率,然后相减。即,对于反向边,有:
[f[cur][i] <- f[cur][i] * f[to][j]
]
[f[cur][i + j] <- (-f[cur][i] * f[to][j])
]
其它不变。
关键代码:
void dfs(int cur, int faa) {
siz[cur] = 1;
for (register int i = head[cur]; i; i = e[i].nxt) {
int to = e[i].to; if (to == faa) continue;
dfs(to, cur);
for (register int j = 0; j <= siz[cur] * 3; ++j) {
for (register int k = 0; k <= siz[to] * 3; ++k) {
if (i & 1)//son -> father
ADD(tmp[j], f[cur][j] * f[to][k] % P),
ADD(tmp[j + k], P - f[cur][j] * f[to][k] % P);
else//father -> son
ADD(tmp[j + k], f[cur][j] * f[to][k] % P);
}
}
siz[cur] += siz[to];
for (register int j = 0; j <= siz[cur] * 3; ++j)
f[cur][j] = tmp[j], tmp[j] = 0;
}
for (register int i = 1; i <= siz[cur] * 3; ++i)
f[cur][i] = f[cur][i] * inv[i] % P;
}
...
f[i][1] = 1ll * a * inv % P, f[i][2] = 2ll * b * inv % P;
f[i][3] = 3ll * c * inv % P;
一边听课一遍写题解好难受啊