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;
}

  

posted @ 2018-09-24 15:36  zzd233  阅读(410)  评论(0编辑  收藏  举报