#树形dp,二次扫描换根法#洛谷 4284 [SHOI2014]概率充电器

题目


分析

充电很难做,考虑判断不充电的概率,
\(dp[x]\)表示点\(x\)无法充电的概率,但是这样有后效性,
考虑先处理子树内的问题,那么
\(dp[x]=(1-p[x])\prod_{y\in son_x}(1-w+dp[y]*w)\)
然后考虑换根,那么将父节点对于新的根的贡献去掉然后求答案,
但是有点恶心的是除数可能为0,所以要特判


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=500011; int as[N],n,k=1;
struct node{
    int y; double w; int next;
}e[N<<1];
double f[N],dp[N],ans;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void dfs1(int x,int fa){
	for (rr int i=as[x];i;i=e[i].next)
	if (e[i].y!=fa) dfs1(e[i].y,x),
	    f[x]*=(1+e[i].w*(f[e[i].y]-1));
}
inline void dfs2(int x,int Fa){
	if (x>1){
		rr double F=0;
		if (1-f[x]!=1/e[Fa].w) F=dp[e[Fa].y]/(1+e[Fa].w*(f[x]-1));
		dp[x]=f[x]*(1+e[Fa].w*(F-1));
	}
	for (rr int i=as[x];i;i=e[i].next)
	    if (i!=Fa) dfs2(e[i].y,i^1);
}
signed main(){
	n=iut();
	for (rr int i=1;i<n;++i){
		rr int x=iut(),y=iut();
		rr double w=iut()*0.01;
		e[++k]=(node){y,w,as[x]},as[x]=k;
		e[++k]=(node){x,w,as[y]},as[y]=k;
	}
	for (rr int i=1;i<=n;++i) f[i]=1-iut()*0.01;
	dfs1(1,0),dp[1]=f[1],dfs2(1,0);
	for (rr int i=1;i<=n;++i) ans+=1-dp[i];
	return !printf("%lf",ans);
}
posted @ 2020-11-04 21:42  lemondinosaur  阅读(62)  评论(0编辑  收藏  举报