BZOJ3566 SHOI2014概率充电器

Problem

BZOJ

Solution

不难写出一个 O ( n 2 ) O(n^2) O(n2)的树形DP,即枚举一个点为根,然后就只要考虑儿子对它的影响了。那么再换根DP一下就行了。

f [ x ] f[x] f[x] x x x不亮且儿子不能使它亮的概率,注意换根时考虑兄弟节点的影响要用条件概率,因为此时已经限制它不会使它的父亲节点亮。所以要记 h [ x ] h[x] h[x]表示x不能点亮它父亲节点的概率。

细节很多,要仔细考虑清楚,可以按其中一个事件是否发生来计算。

h [ x ] = f [ x ] + ( 1 − f [ x ] ) ∗ ( 1 − w ) h[x]=f[x]+(1-f[x])*(1-w) h[x]=f[x]+(1f[x])(1w)

f [ x ] = ( 1 − p x ) ∏ s o n ( 1 − e p ( 1 − f [ v ] ) ) f[x]=(1-p_x)\prod_{son} (1-ep(1-f[v])) f[x]=(1px)son(1ep(1f[v]))

g [ x ] g[x] g[x]表示父亲和兄弟无法点亮x的概率。那么不从兄弟也不从父亲来的概率就是 t = g [ f a ] ∗ f [ f a ] h [ x ] t=g[fa]*\frac {f[fa]} {h[x]} t=g[fa]h[x]f[fa],那么 g [ x ] = t + ( 1 − t ) ∗ ( 1 − w ) g[x]=t+(1-t)*(1-w) g[x]=t+(1t)(1w)

注意特判 h [ x ] = 0 h[x]=0 h[x]=0的情况。

Code

#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=500010;
const double eps=1e-7;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct data{int v,w,nxt;}edge[maxn<<1];
int n,p,head[maxn];
double ans,q[maxn],f[maxn],g[maxn],h[maxn];
void insert(int u,int v,int w)
{
	edge[++p]=(data){v,w,head[u]};head[u]=p;
	edge[++p]=(data){u,w,head[v]};head[v]=p;
}
void input()
{
	int u,v,w;
	read(n);
	for(int i=1;i<n;i++){read(u);read(v);read(w);insert(u,v,w);}
	for(int i=1;i<=n;i++) read(w),q[i]=(double)w/100;
}
void dfs(int x,int pre)
{
	double w;f[x]=1-q[x];
	for(int i=head[x];i;i=edge[i].nxt)
	  if(edge[i].v^pre)
	  {
	  	w=(double)edge[i].w/100;
	  	dfs(edge[i].v,x);
	  	h[edge[i].v]=f[edge[i].v]+(1-f[edge[i].v])*(1-w);
	  	f[x]*=h[edge[i].v];
	  }
}
void dfs2(int x,int pre)
{
	double w,t;
	for(int i=head[x];i;i=edge[i].nxt)
	  if(edge[i].v^pre)
	  {
	  	w=(double)edge[i].w/100;
	  	if(h[edge[i].v]<eps) t=0;
	  	else t=g[x]*f[x]/h[edge[i].v];
	  	g[edge[i].v]=t+(1-t)*(1-w);
	  	dfs2(edge[i].v,x);
	  }
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	input();
	dfs(1,1);
	g[1]=1;
	dfs2(1,1);
	for(int i=1;i<=n;i++) ans+=1-f[i]*g[i];
	printf("%.6lf\n",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值