[SHOI2014] 概率充电器
Description
给定一棵\(N(N\leq 5\times 10^5)\)个节点的树。每个点有概率被直接充电,每条边有概率导电。如果一个没有被直接充电的点通过一条导电的边连接到了某个被充电的点,那么这个点也会被充电。问期望充电的点的个数。
Solution
由期望线性性,我们可以求出每个点被充电的概率,最后求和即可。
节点\(i\)通电有三种可能
- 它自己来电
- 它子树里有一个来电传了过来
- 子树外有一个来电传了过来
可以两遍\(dfs\)做,第一遍先求出第一种和第二种的概率,再换根\(dfs\)加上第三种的概率就行了。这是最开始的\(naive\)想法。
但是这些情况发生的概率不能直接加起来。考虑两个事件\(A,B\),发生的概率分别是\(P(A),P(B)\),那么至少发生一件的概率是\(P(A)+P(B)-P(A)*P(B)\)。
证明就是枚举三种情况:
- \(A\)发生\(B\)不发生,概率为\(P(A)*(1-P(B))\)
- \(A\)不发生\(B\)发生,概率为\((1-P(A))*P(B)\)
- \(A,B\)都发生,概率为\(P(A)*P(B)\)
三种情况求和就是\(P(A)+P(B)-P(A)*P(B)\)了。
知道了这个做两遍\(dfs\)就行了刚做过一个差不多的
啊Typora代码框里的字体好好看啊谁知道这是什么字体啊求告知QAQ
Code
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
using std::vector;
const int N=500005;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)
db f[N],p[N];
int n,cnt,head[N];
struct Edge{
int to,nxt;db dis;
}edge[N<<1];
void add(int x,int y,db z){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
edge[cnt].dis=z;
head[x]=cnt;
}
int getint(){
int X=0,w=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
if(w) return -X;return X;
}
void dfs(int now,int fa){
f[now]=p[now];
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(to==fa) continue;
dfs(to,now);
f[now]=f[now]+f[to]*edge[i].dis-f[now]*f[to]*edge[i].dis;
}
}
void dfs(int now,int fa,db dis){
if(f[now]*dis!=1){
db x=(f[fa]-f[now]*dis)/(1-f[now]*dis);
f[now]=f[now]+x*dis-f[now]*x*dis;
} else f[now]=1;
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(to==fa) continue;
dfs(to,now,edge[i].dis);
}
}
signed main(){
n=getint();
for(int i=1;i<n;i++){
int x=getint(),y=getint(),z=getint();
add(x,y,0.01*z),add(y,x,0.01*z);
}
for(int i=1;i<=n;i++) p[i]=0.01*getint();
dfs(1,0);
for(int i=head[1];i;i=edge[i].nxt)
dfs(edge[i].to,1,edge[i].dis);
db ans=0;
for(int i=1;i<=n;i++) ans+=f[i];
printf("%.6lf\n",ans);
return 0;
}
当你走进这欢乐场