bzoj 3566: [SHOI2014]概率充电器

23 篇文章 0 订阅
说是 树形DP,但也可以说是个搜索+递推。。。。。。不要被DP的标签吓到了。。。。。
先看看题,去年湖北&&上海day1T2 ,当时我都不知道期望是个什么鬼。。。。。还是太年轻了。。。

题目描述

著名的电子产品品牌SHOI刚刚发布了引领世界潮流的下一代电子产品——概率充电器:

“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看吧!”

SHOI概率充电器由n-1条导线连通了n个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。

作为SHOI公司的忠实客户,你无法抑制自己购买SHOI产品的冲动。在排了一个星期的长队之后终于入手了最新型号的SHOI概率充电器。你迫不及待地将SHOI概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?

输入

第一行一个整数:n。概率充电器的充电元件个数。充电元件由1-n编号。

之后的n-1行每行三个整数a, b, p,描述了一根导线连接了编号为a和b的充电元件,通电概率为p%。

第n+2行n个整数:qi。表示i号元件直接充电的概率为qi%。

【数据规模】

对于30%的数据,n≤5000

对于100%的数据,n≤5000000≤p,qi≤100



输出

  输出一行一个实数,为能进入充电状态的元件个数的期望,四舍五入到小数点后6位小数。


charger1.in

charger1.out

3

1 2 50

1 3 50

50 0 0

 

1.000000

 

charger2.in

charger2.out

5

1 2 90

1 3 80

1 4 70

1 5 60

100 10 20 30 40

 

4.300000



我开始想的是一个充电器充了电,让别的充电器充了电,别的充电器又可能让它充了电。。。。于是死循环把自己绕进去了。。。。。

其实树形DP就是两遍dfs。。。。先从根出发dfs,再从叶子节点出发dfs到根
(实际实现和正向dfs一样,只不过先更新数据dfs。。。)
公式推导:自己想一想肿么算概率就可破。。。。。
P.S.进省队好高兴。。。考完省选感觉恍如隔世。。。。
c++:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
const int MAXN=500005;
const double eps=1e-6;
struct edge{
    int from,to;
    double p;
};
vector<edge> edges;
vector<int> g[MAXN];
int n;
bool vis[MAXN];
double ps[MAXN],ans=0,dp[MAXN];
void addedge(int from,int to,int p)
{
    edge e;
    e.from=from;
    e.to=to;
    e.p=(double) p/100.0;
    
    edges.push_back(e);
    e.from=to;
    e.to=from;
    edges.push_back(e);
    int m1=edges.size();
    g[from].push_back(m1-2);
    g[to].push_back(m1-1);
    return ;
}
 
void dfs(int x)
{
    for (int i=0;i<g[x].size();i++)
    {
        edge e=edges[g[x][i]];
        if (vis[e.to]) continue;
        vis[e.to]=1;
        dfs(e.to);
        ps[x]=ps[x]+ps[e.to]*e.p-ps[x]*ps[e.to]*e.p;
    }
    return ;
}
bool cmp(double a,double b) 
    return fabs(a-b)<eps; 
}
void dfs2(int x)
{
    ans+=dp[x];
    for (int i=0;i<g[x].size();i++)
    {
        edge e=edges[g[x][i]];
        if (vis[e.to]) continue;
        vis[e.to]=1;
        double tmp=(1-ps[e.to]*e.p);
        if (cmp(tmp,0)) dp[e.to]=1;
        else {
            double y=(dp[x]-ps[e.to]*e.p)/(1.0-ps[e.to]*e.p); 
            dp[e.to]=ps[e.to] + y*e.p - ps[e.to]*y*e.p; 
        }
        dfs2(e.to);
    }
    return ;
}
 
int main()
{
    scanf("%d",&n);
    int x,y1,z;
    for (int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y1,&z);
        addedge(x,y1,z);
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        ps[i]=(double) x/100.0;
    }
    memset(vis,0,sizeof(vis));
    vis[1]=1;
    dfs(1);
    dp[1]=ps[1];
    memset(vis,0,sizeof(vis));
    vis[1]=1;
    dfs2(1);
    printf("%.6lf\n",ans);
    return 0;
}

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值