和Leo一起做愛數學的好孩子之[SHOI2014]概率充電器
阿新 • • 發佈:2018-12-09
著名的電子產品品牌SHOI 剛剛釋出了引領世界潮流的下一代電子產品—— 概率充電器:
“採用全新納米級加工技術,實現元件與導線能否通電完全由真隨機數決 定!SHOI 概率充電器,您生活不可或缺的必需品!能充上電嗎?現在就試試看 吧!”
SHOI 概率充電器由n-1 條導線連通了n 個充電元件。進行充電時,每條導 線是否可以導電以概率決定,每一個充電元件自身是否直接進行充電也由概率 決定。隨後電能可以從直接充電的元件經過通電的導線使得其他充電元件進行 間接充電。
作為SHOI 公司的忠實客戶,你無法抑制自己購買SHOI 產品的衝動。在排 了一個星期的長隊之後終於入手了最新型號的SHOI 概率充電器。你迫不及待 地將SHOI 概率充電器插入電源——這時你突然想知道,進入充電狀態的元件 個數的期望是多少呢?
額又可恥的看了別人做的
我先想的:考慮子樹對答案的影響:定義狀態為子樹有電,這明顯是不行的。
因為有父親供電,所以這是一個不定方程。
無法狀態合併
正難則反,考慮子樹沒電的狀態。
定義表示子樹沒電,而表示父親未提供電。
很明顯一個點沒電是兩個狀態同時發生:
可以DP對於它有兩種轉移:
1)兒子沒電
2)兒子有電但不連通
但是對於G其並不好轉移
設
則對於
設
我們知道這個式子上面是當前的概率
而:這個概率是考慮了當前子樹的
而這個子樹是沒有考慮的意義的(你在向他轉移)
除掉算F的值就是答案
這個t是沒電的轉移概率
同理有電但不轉移為:
加法原理知:加起來就好了
所以一遍DFS求出F
但是似乎G需要高斯消元
但是:發現對於root沒有父親轉移所以
再次DFS求出G最後就算出來了
#include<bits/stdc++.h> 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<n;++i){ int u,v; double w; scanf("%d%d%lf",&u,&v,&w); add(u,v,w/100.0); add(v,u,w/100.0); } for(int i=1;i<=n;++i)scanf("%lf",&E[i]),E[i]/=100.0; G[1]=1.0; DFS1(1,0); DFS2(1,0); double ans=0; for(int i=1;i<=n;++i){ ans+=(1.0-F[i]*G[i]); } printf("%.6lf",ans); }