LP4284 [SHOI2014]概率充电器 [期望/树形dp]

Author Avatar
空気浮遊 2019年02月16日
  • 在其它设备中阅读本文章

是一道比较水的题,但留了个问题。

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