1 条题解
-
0
自动搬运
来自洛谷,原作者为

asuldb
哭晕了喵搬运于
2025-08-24 21:58:32,当前版本为作者最后更新于2018-08-29 18:45:09,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这是一道概率+树形
首先我们看到这里每一个的贡献都是1,所以我们要求的期望就是概率
求得其实就是这个
为节点通电的概率
显然节点通电有三种可能
-
它自己来电了
-
它的子树里有一个点来电了传了过来
-
它的子树外面有一个点来电了传了过来
第一种情况最好考虑了,至于第二种和第三种我们好像很难解决的样子
但是这显然也告诉了我们这是一个套路题,第二种和第三种正好就是树规里的 思想
于是我们设表示第个节点通电的概率,之后我们利用 思想,在第一遍dfs的过程中,表示通电的概率,且电一定来自它自己或者它的子树里(对应第一第二种情况),在第二遍dfs的时候被更新成为电来自于任何地方的概率(对应所有情况)
最开始初始化,电只能来自自己
之后第一遍dfs,树形dp里的,我们要将子树的信息合并给根,由于根通电还是有两种可能
-
根自己来电了
-
儿子来电,儿子通向根的边导电
显然这两种情况只需要满足一种就够了
但是合并之后的概率是多少呢,直接加起来显然是不对的
而我还真加了起来我们考虑有两个事件,发生的概率分别是,那么至少发生一件的概率应该是
这个怎么推出来的,很简单,至少发生一件,那么就有三种可能
-
发生不发生,那么则为
-
发生不发生,那么则为
-
一起发生,那么则为
三项合起来最后一化就是
所以我们合并根和子树的信息的时候,,是子树的根,是的儿子,是这条边导电的概率
所以
之后我们就要考虑了,一个节点有点也有可能来自它的父亲,于是我们采用的思想用父亲更新儿子
显然我们更新一位父亲的某个儿子,显然我们只能用其他点来电传到父亲的概率来更新这个儿子
于是我们设,而且有
我们要求的是即除了这棵子树其他点来电使得有电的概率
于是解一下这个方程
而之后我们去更新儿子的话还有一边是否导电需要考虑,于是
之后就没有啦,同时还有一个非常坑的地方就是如果
那么除以肯定会出错,由于都已经是1了,显然没有什么必要去更新它了,于是可以直接跳过这一层接着往下更新就好了
#include<iostream> #include<cstring> #include<cstdio> #define re register #define maxn 500005 #define eps 1e-7 struct node { int v,nxt,w; }e[maxn<<1]; int num,n,m; int a[maxn],head[maxn],deep[maxn]; double h[maxn]; double ans; inline int read() { char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } inline void add_edge(int x,int y,int z) { e[++num].v=y; e[num].nxt=head[x]; e[num].w=z; head[x]=num; } void dfs(int x)//up { for(re int i=head[x];i;i=e[i].nxt) if(!deep[e[i].v]) { deep[e[i].v]=deep[x]+1; dfs(e[i].v); double k=h[e[i].v]*double(e[i].w)/100; h[x]=h[x]+k-h[x]*k; } } inline int check(double aa,double bb) { if(aa+eps>bb&&aa-eps<bb) return 1; return 0; } void redfs(int x)//down { ans+=h[x]; for(re int i=head[x];i;i=e[i].nxt) if(deep[e[i].v]>deep[x]) { if(check(h[e[i].v]*double(e[i].w)/100,1)) { redfs(e[i].v); continue; } double k=(h[x]-h[e[i].v]*double(e[i].w)/100)/(1-h[e[i].v]*double(e[i].w)/100); k*=double(e[i].w)/100; h[e[i].v]=h[e[i].v]+k-k*h[e[i].v]; redfs(e[i].v); } } int main() { n=read(); int x,y,z; for(re int i=1;i<n;i++) { x=read(); y=read(); z=read(); add_edge(x,y,z),add_edge(y,x,z); } for(re int i=1;i<=n;i++) a[i]=read(),h[i]=a[i]*0.01; deep[1]=1; dfs(1); redfs(1); printf("%.6lf",ans); return 0; } -
- 1
信息
- ID
- 3257
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者