Codeforces 1045D Interstellar battle 概率期望
原文链接https://www.cnblogs.com/zhouzhendong/p/CF1045D.html
题目传送门 - CF1045D
题意
给定一棵有 $n$ 个节点的树,第 $i$ 个节点有 $p_i$ 的概率消失。有 $q$ 次操作,每次操作修改一个节点消失的概率,请你在每一次操作之后输出树的期望连通块个数。
$n,q\leq 10^5$
题解
首先我们考虑如何求解不操作的情况。
考虑期望的线性性,我们统计每一个节点对答案的负贡献。
首先,假装每一个节点都是一个连通块。
对于节点 x ,如果他消失了,那么连通块个数 -1,由于他消失的概率是 $p_x$ ,他对期望的负贡献为 $p_i$ 。
对于无序数对 $(x,y)$ ,如果 x 和 y 有边连接,那么,当且仅当他们都存在,才会对连通块个数产生负贡献,所以它对期望的负贡献为 $(1-p_x)(1-p_y)$ 。
完成了这个问题之后,修改操作也变得简单了。
首先给树定根。然后,只需要对于每一个节点 x 维护一个 vson[x] 代表其所有儿子的 $(1-p_y)$ 之和。修改操作就变的简单了。
时间复杂度 $O(n)$ 。
代码
#include <bits/stdc++.h> using namespace std; const int N=100005; int n,Q,fa[N]; double v[N],vson[N],ans; vector <int> e[N]; void solve(int x,int pre){ ans-=1.0-v[x]; fa[x]=pre,vson[x]=0; for (auto y : e[x]) if (y!=pre){ ans-=v[x]*v[y]; vson[x]+=v[y]; solve(y,x); } } int main(){ scanf("%d",&n); for (int i=1;i<=n;i++) e[i].clear(); v[0]=0; for (int i=1;i<=n;i++) scanf("%lf",&v[i]),v[i]=1.0-v[i]; for (int i=1,a,b;i<n;i++){ scanf("%d%d",&a,&b),a++,b++; e[a].push_back(b); e[b].push_back(a); } ans=n; solve(1,0); scanf("%d",&Q); while (Q--){ int x; double y; scanf("%d%lf",&x,&y),x++; ans+=v[fa[x]]*vson[fa[x]]+v[x]*vson[x]+1.0-v[x]; vson[fa[x]]-=v[x]; v[x]=1.0-y; vson[fa[x]]+=v[x]; ans-=v[fa[x]]*vson[fa[x]]+v[x]*vson[x]+1.0-v[x]; printf("%.6lf\n",ans); } return 0; }