bzoj3566 [SHOI2014]概率充电器

题目链接:bzoj3566
题目大意:
SHOI 概率充电器由 n-1 条导线连通了 n 个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。问进入充电状态的元件个数的期望是多少呢?
Input
第一行一个整数:n。概率充电器的充电元件个数。充电元件由 1-n 编号。
之后的 n-1 行每行三个整数 a, b, p,描述了一根导线连接了编号为 a和b的
充电元件,通电概率为 p%。
第 n+2 行 n 个整数:qi。表示 i 号元件直接充电的概率为 qi%。

Output
输出一行一个实数,为进入充电状态的元件个数的期望,四舍五入到六位小数

题解:
树形dp+概率dp
啊下面这句话明明就有歧义啊

随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。

题目又有说自己直接进行充电有个概率。还以为只有自己能充电的才能通过导线帮别人充电。而实际上,自己是别人通过导线把电能传给你让你也能充电的也可以帮别人充电..(有点绕?

那么对于一个点来说,所有与它相连的都可能影响它。而这是一棵树,那么我们分成两个部分,一个是儿子影响你的,一个是父亲影响你的。
设f[i]表示i的儿子不能给i充电的概率,g[i]表示i的父亲不能给i充电的概率。
那么答案就等于 ni=1(1f[i]×g[i]×(1q[i]))
[q[i]是给出的i能自己充电的概率

于是我们需要dfs两遍来分别求f和g。因为一个是由上至下一个是由下至上的。
然后我就不知道该说什么了。
f,g就是根据含义来求啊。
方程什么的还是要自己推的好。

如果还不会就看代码吧。不过可能我的式子比较复杂?

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 500010

const double eps=1e-6;
double q[maxn],f[maxn],g[maxn];
//f[i] 儿子不能给i充到电的概率
//g[i] 父亲不能给i充到电的概率
struct node
{
    int x,y,next;double p;
}a[maxn*2];int len,first[maxn];
void ins(int x,int y,double p)
{
    len++;a[len].x=x;a[len].y=y;
    a[len].p=p;a[len].next=first[x];first[x]=len;
}
void trp1(int x,int fa)
{
    for (int k=first[x];k!=-1;k=a[k].next)
    {
        int y=a[k].y;
        if (y==fa) continue;
        trp1(y,x);
        double ls=(1-(1-q[y])*f[y])*a[k].p;
        //(1-q[y])*f[y]就表示y不能充电的概率
        //ls表示y能为x充电的概率
        f[x]*=1-ls;
    }
}
void trp2(int x,int fa)
{
    for (int k=first[x];k!=-1;k=a[k].next)
    {
        int y=a[k].y;
        if (y==fa) continue;
        double ls=(1-(1-q[y])*f[y])*a[k].p;
        //ls表示y能为x充电的概率
        if (1-ls>eps) ls=f[x]/(1-ls);
        //这里ls就表示除了y这个孩子外,x的其他孩子不能给x充电的概率
        g[y]*=1-a[k].p*(1-(1-q[x])*g[x]*ls);
        //(1-q[x])*g[x]*ls就表示x不能充电的概率
        trp2(y,x);
    }
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int n,x,y,i;double p,ans=0;
    scanf("%d",&n);
    len=0;memset(first,-1,sizeof(first));
    for (i=1;i<n;i++)
    {
        scanf("%d%d%lf",&x,&y,&p);
        p/=100.0;ins(x,y,p);ins(y,x,p);
    }
    for (i=1;i<=n;i++)
    {
        scanf("%lf",&q[i]);
        q[i]/=100.0;g[i]=f[i]=1.0;
    }
    trp1(1,0);trp2(1,0);
    for (i=1;i<=n;i++)
        ans+=1-(1-q[i])*f[i]*g[i];
    printf("%.6lf\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值