luogu食用,风味更佳,这条横线懒得删了:传送门
这题老师N年前就讲了,最近才做
让我们步入正题
话说为什么会想到概率期望dp+树形dp呢,首先我们来观察题目,发现它的每一个点之间都只有一条边,而且n-1条边也告诉我们,这是一颗完全二叉树。
而且每次充电的几率与导电的概率是给出的本文中用electricity数组表示给自己充电的概率 图的边权表示电线通电的概率,文章中暂且用b[a−>x]表示a指向b的边
那么第一个问题就是如何建树,正常人应该都会使用邻接表吧,在这里不做赘述。
既然是dp,那么我们应该考虑到dp绕不过去的一个问题:状态是什么。
我们从题意中可以知道,一个点有电的概率无非只有三种情况
既然都说是树形dp了,那么按照树形dp的思想,我们应该把爸爸先扔在一边,只考虑儿子给他充电的情况,在本文中我们用FFF数组表示,那么不可避免的,还要设一个数组表示他爸爸给他充电的概率,本文中用G数组表示
所以状态设计:
但是我们这么做就会遇到一个问题:概率该怎么相加呢?(我也不知道)很明显,这是一个难题,怎么办?既然这一种状态设计不够优秀,那么我们就换一种等价的设计方案:他没有电的概率,显然,有电的概率就是(1-他没有电的概率)
所以更换后的状态设计就是
bingo,我们就这么获得了一个优秀的状态设计。 那么我们就来考虑状态的转移,先从比较简单的F数组开始
首先,大家想必都知道这两个公式
那么FFF数组的状态转移方程就显而易见了
解释一下:一个点的自己或自己的儿子不给他充电有一种先决条件
而他没有电的概率就有多种情况了
然后由于要一颗子树中的所有点都满足这一条件,所以要使用累乘符号∏
同样他的求出也是非常简单的只要dfsdfsdfs遍历一边图就可以了
这个就比较麻烦了,这个我们先来看看一个点最终没有电概率,本文中用ttt表示
解释一下:一个点xxx没有电的概率是要满足一下两个条件
然而由于我们计算GGG数组的时候也乘上了一个 F[x]+(1-F[x])(1-b[father−>x]),所以为了防止出现类似死循环的情况,需要把他再次除去。
那么通过乘法原理,就可以得出一个点xxx没有电的最终概率为
这个转移类比FFF数组的状态转移
然后通过类似高斯消元的方法就可以求出GGG[x]了(不会高斯消元的同学不必担心,因为这在代码中并没有用到,只要在dfsdfsdfs的是后顺便求出就可以了)
这一部分想必我不说大家都知道了 根据乘法原理,一个点最终没有电的概率为
所以一个点xxx有电的概率就自然是
注意本题的数据类型要多开double的数据,而int类型的数据就算除去了一个100.0,还是int类型,会自动舍去小数部分(作者就是因为这个错误卡了好久)
(2984ms(不开O2),41.72MB,时间复杂度O(n))
#include
using namespace std;
struct cutetree {
int to,nxt;
double value;
}e[1000020]={};
int linkk[500010]={};
int len;
double electricity[500010]={};
int x,y;
int elec;
double G[500010]={};
double F[500010]={};
bool flag[500010]={};
int n;
void insert(int x,int y,double value) {
e[++len].value=value;
e[len].to=y;
e[len].nxt=linkk[x];
linkk[x]=len;
}
void dfs(int x) {
flag[x]=true;
F[x]=1.0-electricity[x];
for(int i=linkk[x];i;i=e[i].nxt)
{
if(flag[e[i].to]) continue;
dfs(e[i].to);
F[x]*=F[e[i].to]+(1.0-F[e[i].to])*(1.0-e[i].value);
}
flag[x]=false;
}
void findf(int u) {
flag[u]=true;
for(int i=linkk[u];i;i=e[i].nxt)
{
if(flag[e[i].to]) continue;
double t=G[u]*F[u]/(F[e[i].to]+(1.0-F[e[i].to])*(1.0-e[i].value));
G[e[i].to]=t+(1.0-t)*(1.0-e[i].value);
findf(e[i].to);
}
}
int main() {
scanf("%d",&n);
for(int i=1;iscanf("%d%d%d",&x,&y,&elec);
insert(x,y,elec/100.0);
insert(y,x,elec/100.0);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&elec);
electricity[i]=elec/100.0;
}
G[1]=1.0;
dfs(1);
findf(1);
double ans;
for(int i=1;i<=n;i++)
ans+=(1.0-G[i]*F[i]);
printf("%.6lf\n",ans);
return 0;
}