zoukankan      html  css  js  c++  java
  • BZOJ3566: [SHOI2014]概率充电器 树形+概率dp

    3566: [SHOI2014]概率充电器

    Time Limit: 40 Sec  Memory Limit: 256 MB
    Submit: 1888  Solved: 857
    [Submit][Status][Discuss]

    Description

    著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器:
    “采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看吧!

    SHOI 概率充电器由 n-1 条导线连通了 n 个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。
    随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。
    作为 SHOI 公司的忠实客户,你无法抑制自己购买 SHOI 产品的冲动。在排了一个星期的长队之后终于入手了最新型号的 SHOI 概率充电器。
    你迫不及待地将 SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?

    Input

    第一行一个整数:n。概率充电器的充电元件个数。充电元件由 1-n 编号。
    之后的 n-1 行每行三个整数 a, b, p,描述了一根导线连接了编号为 a 和 b 的
    充电元件,通电概率为 p%。
    第 n+2 行 n 个整数:qi。表示 i 号元件直接充电的概率为 qi%。

    Output

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

     
    题意:给你一颗树,每个点有一定的概率被直接导通,每条边也有一定概率导通,每个点可以通过相连的边在另一个点导通情况下以一定概率导通。求整棵树导通点个数的期望。
    题解:首先期望这里等于每个点导通概率和。由于导通情况很多,正着计算非常困难,所以不妨正难则反,考虑每个点无法导通的情况。显然,因为是一棵树,根据大部分树形dp的套路,稍加思索发现这里存在父亲向儿子的转移,也存在儿子向父亲的转移。
    定义f[i]表示i这个点由儿子无法转移的概率。显然有:f[i]=(1-p[i])*Π(f[v]+(1-f[v])*(1-val[i]));
    把累乘里的东西定义为h[i]。
    这题难点主要是父亲向儿子的转移。定义g[i]为i的父亲无法向i转移的概率。
    tmp=g[fa[i]]∗f[fa[i]]/h[i];
    然后g[i]=tmp+(1-tmp)*(1-val[i]);//这里val[i]是i的父亲到i的那条边,意会一下。
    为什么tmp是这样的呢,画个图就很明白了。
    因为这里g[i]可能是父亲链上转移过来的,也可能是从i的兄弟通过父亲转移过来,所以真正这个转移时我们的目标是对着这条边。所以把f[fa[i]]/h[i]就是除h[i]之外的所有兄弟使得fa[i]不能点亮的概率,再乘以g[fa[i]]就是不从父亲链也不从兄弟转移过来的概率,然后g[i]的计算就变得显然了。
    #include<bits/stdc++.h>
    #define ll long long
    #define pb push_back
    #define _mp make_pair
    #define db double
    #define eps 1e-9
    using namespace std;
    const int maxn=5e5+100;
    const int inf=1e6;
    inline ll read()
    {
    	ll x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    int cnt,tot;
    int n,m;
    int fir[maxn],nxt[maxn*2],to[maxn*2];
    db val[maxn*2],f[maxn],g[maxn],h[maxn],p[maxn];
    void add_e(int x,int y,db k)
    {
    	++cnt;nxt[cnt]=fir[x];fir[x]=cnt;to[cnt]=y;val[cnt]=k;
    }
    void dfs1(int x,int fa)
    {
    	f[x]=p[x];
    	for(int i=fir[x];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v==fa)continue;
    		dfs1(v,x);
    		h[v]=(1.0-f[v])*(1-val[i])+f[v];
    		f[x]*=h[v];
    	}
    }
    void dfs2(int x,int fa)
    {
    	db sum=p[x];int sz=0;
    	for(int i=fir[x];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v==fa)continue;
    		if(h[v]>eps)
    		{
    			sum*=h[v];
    		}
    		else sz++;
    	}
    	for(int i=fir[x];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v==fa)continue;
    		db tmp;
    		if(h[v]>eps)
    		{
    			tmp=(sz?0:sum/h[v]*g[x]);
    		}
    		else tmp=(sz>1?0:sum*g[x]);
    		g[v]=tmp+(1.0-tmp)*(1-val[i]);
    		dfs2(v,x);
    	}
    }
    int main()
    {
    	cnt=0;
    	n=read();
    	int u,v,w;
    	for(int i=1;i<n;i++)
    	{
    		u=read();v=read();w=read();
    		db kk=1.0*w/100.0;
    		add_e(u,v,kk);
    		add_e(v,u,kk);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		w=read();
    		p[i]=1.0-1.0*w/100;
    	}
    	dfs1(1,0);g[1]=1.0;dfs2(1,0);
    	db ans=0;
    	for(int i=1;i<=n;i++)
    	{
    		ans+=1.0-f[i]*g[i];
    	}
    	printf("%.6f
    ",ans);
    }
    

      

  • 相关阅读:
    第二十一章流 1流的操作 简单
    第二十章友元类与嵌套类 1友元类 简单
    第十九章 19 利用私有继承来实现代码重用 简单
    第二十章友元类与嵌套类 2嵌套类 简单
    第十九章 8链表类Node 简单
    第二十一章流 3用cin输入 简单
    第十九章 10 图书 药品管理系统 简单
    第十九章 11图书 药品管理系统 简单
    第二十一章流 4文件的输入和输出 简单
    第十九章 12 什么时候使用私有继承,什么时候使用包含 简单
  • 原文地址:https://www.cnblogs.com/intwentieth/p/9790207.html
Copyright © 2011-2022 走看看