洛谷 P4284 [SHOI2014]概率充電器 解題報告
P4284 [SHOI2014]概率充電器
題目描述
著名的電子產品品牌SHOI 剛剛發布了引領世界潮流的下一代電子產品—— 概率充電器:
“采用全新納米級加工技術,實現元件與導線能否通電完全由真隨機數決 定!SHOI 概率充電器,您生活不可或缺的必需品!能充上電嗎?現在就試試看 吧!”
SHOI 概率充電器由 \(n-1\) 條導線連通了 \(n\) 個充電元件。進行充電時,每條導線是否可以導電以概率決定,每一個充電元件自身是否直接進行充電也由概率決定。隨後電能可以從直接充電的元件經過通電的導線使得其他充電元件進行 間接充電。
作為 SHOI 公司的忠實客戶,你無法抑制自己購買 SHOI 產品的沖動。在排了一個星期的長隊之後終於入手了最新型號的 SHOI 概率充電器。你迫不及待地將 SHOI 概率充電器插入電源——這時你突然想知道,進入充電狀態的元件個數的期望是多少呢?
輸入輸出格式
輸入格式:
第一行一個整數:\(n\)。概率充電器的充電元件個數。充電元件由 \(1-n\) 編號。
之後的 \(n-1\) 行每行三個整數\(a,b,p\) ,描述了一根導線連接了編號為 \(a\) 和 \(b\) 的 充電元件,通電概率為\(p\%\)。
第 \(n+2\) 行 \(n\) 個整數:\(q_i\)。表示 \(i\) 號元件直接充電的概率為 \(q_i\%\)。
輸出格式:
輸出一行一個實數,為能進入充電狀態的元件個數的期望,四舍五入到小數點後6位小數。
說明
對於\(30\%\)的數據,\(n≤5000\)。
對於\(100\%\)的數據,\(n≤500000\),\(0≤p,q_i≤100\)
果然一做期望題就暴露自己腦子蠢的事實了=-=
首先我讀錯題了,但是我覺得不能怪我啊,難道題目說的不就是只有自己激發的才能傳火給別人嘛...
然後我花了好久才發現這個題就是在算概率。
每個點可以從所有其他點獲取傳火概率,做一個換跟dp就行了,具體的
\(dp_i\)先處理出子樹\(i\)的貢獻,然後直接換跟做就可以了。
需要註意的地方,傳火的時候要減去兩個都有,換跟中有一個類似退背包的過程需要處理分母為0
Code:
#include <cstdio> #include <cmath> const int N=5e5+10; const double eps=1e-8; int head[N],to[N<<1],Next[N<<1],cnt; double dp[N],ans,edge[N<<1]; void add(int u,int v,double w) { to[++cnt]=v,edge[cnt]=w,Next[cnt]=head[u],head[u]=cnt; } void dfs1(int now,int fa) { for(int v,i=head[now];i;i=Next[i]) if((v=to[i])!=fa) { dfs1(v,now); dp[now]=dp[now]+dp[v]*edge[i]-dp[now]*dp[v]*edge[i]; } } void dfs2(int now,int fa,double p) { dp[now]=dp[now]+p-dp[now]*p; ans+=dp[now]; for(int v,i=head[now];i;i=Next[i]) if((v=to[i])!=fa) { if(fabs(dp[v]*edge[i]-1)<eps) p=dp[now]; else p=(dp[now]-dp[v]*edge[i])/(1-dp[v]*edge[i]); dfs2(v,now,p*edge[i]); } } int main() { int n;scanf("%d",&n); for(int u,v,w,i=1;i<n;i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,1.0*w/100),add(v,u,1.0*w/100); } for(int i=1;i<=n;i++) scanf("%lf",dp+i),dp[i]/=100.0; dfs1(1,0); dfs2(1,0,0); printf("%.6lf\n",ans); return 0; }
2019.1.13
洛谷 P4284 [SHOI2014]概率充電器 解題報告