[SHOI 2014] 概率充电器

本文详细解析了洛谷4284题(BZOJ3566)的解题思路与算法实现,通过概率论公式P(A+B)=P(A)+P(B)-P(AB)结合深度优先搜索解决节点间充电概率问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

QAQ…

题目分析:

P(A+B)=P(A)+P(B)P(AB) P ( A + B ) = P ( A ) + P ( B ) − P ( A B )
首先可以将元件能否充电分成3种情况考虑

1、它自己给自己充好了电

2、它的儿子方向给它传送了电

3、它的父亲方向给它传送了电。

1 是输入的
2 可以通过一次dfs做
3 麻烦一点,因为2中我们已经求得当前点(now)的儿子们给当前点的贡献,现在要求当前点给它的某个儿子(to)的贡献,这里要计算的话,就必须要排除之前to->now的,(想想若2中计算了to->now的概率,现在又要计算now->to的概率,明显出现了循环)

设y = now点除了从to点来的电 的充电概率,dp[i]表示i点的充电概率,pson[i]表示在i的子树中,i的充电概率

pson[to]p+ypson[to]py=dp[now] p s o n [ t o ] ∗ p + y − p s o n [ t o ] ∗ p ∗ y = d p [ n o w ]

(1pson[to]p)y=dp[now]pson[to]p ( 1 − p s o n [ t o ] ∗ p ) ∗ y = d p [ n o w ] − p s o n [ t o ] ∗ p

y=(dp[now]pson[to]p)/(1pson[to]p); y = ( d p [ n o w ] − p s o n [ t o ] ∗ p ) / ( 1 − p s o n [ t o ] ∗ p ) ;

dp[to]=pson[to]+yppson[to]yp; d p [ t o ] = p s o n [ t o ] + y ∗ p − p s o n [ t o ] ∗ y ∗ p ;

别忘了卡精度…

题目链接:

Luogu 4284
BZOJ 3566

Ac 代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
const int maxm=510000;
const double eps=1e-8;
int head[maxm],net[maxm<<1],to[maxm<<1],cnt;
double p[maxm],dp[maxm],cost[maxm<<1],ans;
int n;
inline void addedge(int u,int v,double c)
{
    cnt++;
    to[cnt]=v,cost[cnt]=c,net[cnt]=head[u],head[u]=cnt;
}
inline bool check(double a,double b)
{
    return std::fabs(a-b)<eps;
}
void dfs1(int now,int fa)
{
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fa)
    {
        dfs1(to[i],now);
        p[now]=p[now]+p[to[i]]*cost[i]-p[now]*p[to[i]]*cost[i];
    }
}
void dfs2(int now,int fa)
{
    ans+=dp[now];
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fa)
    {
        double P=cost[i];
        double tmp=(1.0-p[to[i]]*P);
        if(check(tmp,0)) dp[to[i]]=1.0;  
        else  
        {  
            double y=(dp[now]-p[to[i]]*P)/tmp;  
            dp[to[i]]=p[to[i]]+y*P-p[to[i]]*y*P;  
        }  
        dfs2(to[i],now); 
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int u,v;
        double c;
        scanf("%d%d%lf",&u,&v,&c);
        addedge(u,v,c/100),addedge(v,u,c/100);
    } 
    for(int i=1;i<=n;i++)
     scanf("%lf",&p[i]),p[i]/=100;
    dfs1(1,0);
    dp[1]=p[1];
    dfs2(1,0);
    printf("%.6lf\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值