概率充电器
题解
很简单的一道树形dp,不过我们得用容斥的方法转变一下思路。
如果我们将dp值设为灯亮的几率,我们的时间复杂度可能被卡到,显然是不行的,我们也就不具体分析了。
我们设为ti不被它的子树上任意一点亮的概率(含自身)。
很显然,dp1(以下省略1)的转移方程式如下:
u点不能被点亮的情况为它自身不亮且它的儿子不能将它点亮的概率的乘积。
然后,我们可以用换根的想法改一改,设当前的u为根。即为i点不被点亮的概率。
很显然,dp2(一下令dp1为f,省略2)的转移方程式如下:
v为u的子节点,接下来我们将v换为根。
P为v为根时u子树点不亮v的概率:
那么,答案即为。
源码
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#define re register
#define MAXN 500005
using namespace std;
typedef long long LL;
#define int LL
const int INF=0x7f7f7f7f;
int from[MAXN<<1],to[MAXN<<1],nxt[MAXN<<1];
int head[MAXN],tot,n;
double paid[MAXN<<1],q[MAXN],ans;
double dp1[MAXN],dp2[MAXN];
#define gc() getchar()
template<typename _T>
inline void read(_T &x){
_T f=1;x=0;char s=gc();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
x*=f;
}
void addEdge(int u,int v,double w){
from[++tot]=u;to[tot]=v;paid[tot]=w;
nxt[tot]=head[u];head[u]=tot;
}
void dfs1(int u,int fa){
dp1[u]=1.0-q[u];
for(int i=head[u];i;i=nxt[i]){
int v=to[i];double w=1.0-paid[i];
if(v==fa) continue;
dfs1(v,u);dp1[u]*=(w+dp1[v]-w*dp1[v]);
}
}
void dfs2(int u,int fa){
for(int i=head[u];i;i=nxt[i]){
int v=to[i];double w=1.0-paid[i];
if(v==fa) continue;
double P=dp2[u]/(w+dp1[v]-w*dp1[v]);
dp2[v]=dp1[v]*(w+P-w*P);
dfs2(v,u);
}
}
signed main()
{
read(n);
for(int i=1;i<n;i++){
int u,v,w;read(u);read(v);read(w);
addEdge(u,v,0.01*w);addEdge(v,u,0.01*w);
}
for(int i=1;i<=n;i++) scanf("%lf",&q[i]),q[i]*=0.01;
dfs1(1,0);dp2[1]=dp1[1];dfs2(1,0);
for(int i=1;i<=n;i++) ans+=1-dp2[i];
printf("%.6lf",ans);
return 0;
}