著名的电子产品品牌SHOI 刚刚发布了引领世界潮流的下一代电子产品—— 概率充电器:
“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决 定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看 吧!”
SHOI 概率充电器由n-1 条导线连通了n 个充电元件。进行充电时,每条导 线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率 决定。随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行 间接充电。
作为SHOI 公司的忠实客户,你无法抑制自己购买SHOI 产品的冲动。在排 了一个星期的长队之后终于入手了最新型号的SHOI 概率充电器。你迫不及待 地将SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件 个数的期望是多少呢?
额又可耻的看了别人做的
我先想的:考虑子树对答案的影响:定义状态为子树有电,这明显是不行的。
因为有父亲供电,所以这是一个不定方程。
无法状态合并
正难则反,考虑子树没电的状态。
定义表示子树没电,而表示父亲未提供电。
很明显一个点没电是两个状态同时发生:
可以DP对于它有两种转移:
1)儿子没电
2)儿子有电但不连通
但是对于G其并不好转移
设
则对于
我们知道这个式子上面是当前的概率
而:这个概率是考虑了当前子树的
而这个子树是没有考虑的意义的(你在向他转移)
除掉算F的值就是答案
这个t是没电的转移概率
同理有电但不转移为:
加法原理知:加起来就好了
所以一遍DFS求出F
但是似乎G需要高斯消元
但是:发现对于root没有父亲转移所以
再次DFS求出G最后就算出来了
#include
using namespace std;
const int N=1e6+100;
struct Front_star{
int u,v,nxt;
double w;
}e[N<<2];
int cnt=0;
int first[N];
void add(int u,int v,double w){
cnt++;
e[cnt].u=u;
e[cnt].v=v;
e[cnt].w=w;
e[cnt].nxt=first[u];
first[u]=cnt;
}
double F[N];
double G[N];
double E[N];
void DFS1(int u,int fat){
F[u]=(1.0-E[u]);
for(int i=first[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==fat)continue;
DFS1(v,u);
F[u]=F[u]*(F[v]+(1.0-F[v])*(1.0-e[i].w));
}
}
void DFS2(int u,int fat){
for(int i=first[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==fat)continue;
double t=F[u]*G[u]/(F[v]+(1.0-F[v])*(1.0-e[i].w));
G[v]=t+(1.0-t)*(1.0-e[i].w);
DFS2(v,u);
}
}
int n;
int main(){
scanf("%d",&n);
for(int i=1;i