Bzoj3566:[SHOI2014]概率充電器:概率dp

題目鏈接[SHOI2014]概率充電器

woc我的高考概率是怎麼學的這麼簡單的概率公都忘了233

公式:設事件A,B,p爲概率,則

p(A|B)=p(A)+p(B)-p(A&B)

意思是發生A或B事件之一的概率是發生A的概率+發生B的概率-A和B同時發生概率

那麼一個點x有電的情況爲1:自己給自己充上了電;2:子節點給他充上了電;3:父節點給他充上了電

設事件1的概率爲p[x],那麼1、2使得x有電的概率可以直接使用公式dfs求出來

現在考慮3,我們從父節點貢獻到子節點時要把子節點貢獻給父節點的貢獻減去,公式變一下形即可算出

然後在正向用公式合併即可

注意除法的時候判斷除數大小,被坑了

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500010;
int n,m,tot=1,h[maxn];
struct edge{int to,next; double w;}G[maxn*2];
double p[maxn],dp[maxn];

void dfs1(int x,int fat){
	for (int i=h[x];i;i=G[i].next){
		int v=G[i].to;
		if (v==fat) continue;
		dfs1(v,x);
		p[x]=p[x]+p[v]*G[i].w-p[x]*p[v]*G[i].w;//p(A|B)=p(A)+p(B)-p(A&B);
	}
}

void dfs2(int x,int fat){
	for (int i=h[x];i;i=G[i].next){
		int v=G[i].to;
		if (v==fat) continue;
		double tmp=1-p[v]*G[i].w;
		if (abs(tmp)<1e-10) dp[v]=1;//這裏要注意
		else{
			double tmp2=(dp[x]-p[v]*G[i].w)/tmp;
			dp[v]=p[v]+tmp2*G[i].w-p[v]*tmp2*G[i].w;
		}
		dfs2(v,x);
	}
}

void add(int x,int y,double z){
	G[++tot].to=y;G[tot].next=h[x];h[x]=tot;G[tot].w=z;
	G[++tot].to=x;G[tot].next=h[y];h[y]=tot;G[tot].w=z;
}

int main(){
	scanf("%d",&n);
	for (int i=1;i<n;++i){
		int x,y; double z;
		scanf("%d%d%lf",&x,&y,&z);
		z/=100.0; add(x,y,z);
	}
	for (int i=1;i<=n;++i) scanf("%lf",&p[i]),p[i]/=100.0;
	dfs1(1,-1); dp[1]=p[1]; dfs2(1,-1);
	double ans=0;
	for (int i=1;i<=n;++i) ans+=dp[i];
	printf("%.6lf",ans);
}


發佈了112 篇原創文章 · 獲贊 12 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章