1. 程式人生 > >BZOJ3566 概率充電器

BZOJ3566 概率充電器

Link

Difficulty

演算法難度4,思維難度5,程式碼難度4

Description

給定一棵樹,每個點有直接充電的概率,每條邊有傳遞充電的概率

求被充電的點的個數的期望值

1n5×1051\le n\le 5\times 10^5

Solution

換根dp裸題咯

定義dpxdp_x代表點xx充不上點的概率,ans=ndpians=n-\sum dp_i

轉移方程:dpx=(1px)×uson(x)(1vali+vali×dpu)dp_x=(1-p_x)\times\prod_{u\in son(x)}(1-val_i+val_i\times dp_u)

換根的式子直接從dp轉移方程就能看出來的

聽說卡精度?我沒什麼感覺。

時間複雜度O(n)O(n)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
inline int read()
{ int x=0,f=1;char ch=' '; while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return f==1?x:-x; } const int N=1e6+5; int n,tot; int head[N],to[N],Next[N]; double ans,val[N],p[N],dp[N]; inline
void addedge(int x,int y,double c){ to[++tot]=y; Next[tot]=head[x]; head[x]=tot; val[tot]=c; } inline void dfs(int x,int fa){ dp[x]=1.0-p[x]; for(int i=head[x];i;i=Next[i]){ int u=to[i]; if(u==fa)continue; dfs(u,x); dp[x]*=1.0-val[i]+val[i]*dp[u]; } } inline void dfs2(int x,int fa){ ans+=1.0-dp[x]; for(int i=head[x];i;i=Next[i]){ int u=to[i]; if(u==fa)continue; dp[x]/=1.0-val[i]+val[i]*dp[u]; dp[u]*=1.0-val[i]+val[i]*dp[x]; dfs2(u,x); dp[u]/=1.0-val[i]+val[i]*dp[x]; dp[x]*=1.0-val[i]+val[i]*dp[u]; } } int main(){ n=read(); for(int i=1;i<n;++i){ int x=read(),y=read(),c=read(); double l=(double)c/100.0; addedge(x,y,l);addedge(y,x,l); } for(int i=1;i<=n;++i){ int c=read(); p[i]=(double)c/100.0; } dfs(1,0); dfs2(1,0); printf("%.6lf\n",ans); return 0; }