题意:给出一颗n个点,n-1条边的树,每个结点都有其初始化的被点亮的概率,每条边也有其可以通电的概率,求能被点亮的灯的数量的期望。
分析与总结:通过这道树形dp,加深了对换根的理解,对于解决一些有依赖性关系的问题,我们可以考虑高斯消元/换根dp,主要套路为先一遍dfs求出每个结点只考虑子节点中贡献,那么作为根,它的答案就是和我们想求的一致的。然后考虑换根,建议画图理解,主要思想是将父节点中的贡献排除这个点的贡献(在这里是个条件概率公式),然后这个点依赖是它的子树和父节点连接的那一坨的贡献。
Latex太烦了。
贴个手写学习笔记
const int N = 5e5 + 5;
vector<pair<int, double> >G[N];
double q[N], f[N], g[N];
void dfs(int u, int fa)
{
f[u] = (1 - q[u]);
for (auto i : G[u])
{
if (i.first == fa)continue;
dfs(i.first, u);
f[u] *= (1 - i.second + i.second*f[i.first]);
}
}
double another(int u, int fa, double w)
{
return g[fa] / (1 - w + w * f[u]);
}
void dfs2(int u, int fa)
{
if (u == 1)g[u] = f[u];
for (auto i : G[u])
{
if (i.first == fa)continue;
g[i.first] = f[i.first] * (1 - i.second + i.second*another(i.first, u, i.second));
dfs2(i.first, u);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int n;cin >> n;
f(i, 1, n - 1)
{
int a = in(), b = in(), p = in();
double gp = 1.0*p / 100;
G[a].emplace_back(pair<int, double>{b, gp});
G[b].emplace_back(pair<int, double>{a, gp});
}
f(i, 1, n) {
int p = in();
q[i] = 1.0*p / 100;
}
dfs(1, -1);
dfs2(1, -1);
double res = 0.0;
f(i, 1, n)res += 1 - g[i];
printf("%.6lf\n", res);
return 0;
}