LP4284 [SHOI2014]概率充电器 [期望/树形dp]
是一道比较水的题,但留了个问题。
Solution
很显然的可以可看出来算出每个点单独的被点亮的概率,然后把它们加起来就是期望。
但是这里我有点卡着了。因为不知道是什么公式可以解释他们加起来就是期望。贡献么?算它们产生贡献的概率乘上它们的贡献?似乎我并没有找到相关的东西来证明可以这么做。可能对期望的数学理论的东西了解太少了。
如果不管这个问题就是一个很裸的换根 dp 了。令 $f_x$ 为 $x$ 被子树点亮的概率,那么:$$f_x=1-(1-p_x)\prod \limits_{(x, v), v\in son_x} (1-W_{(x, v)}+W_{(x, v)}(1-f_v))$$
这里注意一下如果边和点的概率都有 $1.0$,那么需要往上减一个较小的值避免信息丢失。
Code
// Code by ajcxsu
// Problem: charge
#include<bits/stdc++.h>
using namespace std;
#define double long double
const int N=5e5+10;
int h[N], to[N<<1], nexp[N<<1], p=1;
double W[N<<1], v[N], f[N];
inline void ins(int a, int b, double w) { nexp[p]=h[a], h[a]=p, to[p]=b, W[p]=w, p++; }
void dfs(int x, int fr) {
f[x]=1;
for(int u=h[x];u;u=nexp[u])
if(to[u]!=fr) {
dfs(to[u], x);
f[x]*=(1-W[u]+W[u]*(1-f[to[u]]));
}
f[x]*=1-v[x], f[x]=1-f[x];
}
double ans;
void dfs2(int x, int fr) {
ans+=f[x];
for(int u=h[x];u;u=nexp[u])
if(to[u]!=fr) {
f[x]=1-(1-f[x])/(1-W[u]+W[u]*(1-f[to[u]]));
f[to[u]]=1-(1-f[to[u]])*(1-W[u]+W[u]*(1-f[x]));
dfs2(to[u], x);
f[to[u]]=1-(1-f[to[u]])/(1-W[u]+W[u]*(1-f[x]));
f[x]=1-(1-f[x])*(1-W[u]+W[u]*(1-f[to[u]]));
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int n, a, b;
double p;
cin>>n;
for(int i=0;i<n-1;i++) cin>>a>>b>>p, p/=100, p-=p==1.0?1e-11:0, ins(a, b, p), ins(b, a, p);
for(int i=1;i<=n;i++) cin>>v[i], v[i]/=100, v[i]-=v[i]==1.0?1e-11:0;
dfs(1, 0), dfs2(1, 0);
cout.setf(ios::fixed), cout<<setprecision(6)<<ans<<endl;
return 0;
}