題目鏈接:bzoj3566
題目大意:
SHOI 概率充電器由 n-1 條導線連通了 n 個充電元件。進行充電時,每條導線是否可以導電以概率決定,每一個充電元件自身是否直接進行充電也由概率決定。隨後電能可以從直接充電的元件經過通電的導線使得其他充電元件進行間接充電。問進入充電狀態的元件個數的期望是多少呢?
Input
第一行一個整數:n。概率充電器的充電元件個數。充電元件由 1-n 編號。
之後的 n-1 行每行三個整數 a, b, p,描述了一根導線連接了編號爲 a和b的
充電元件,通電概率爲 p%。
第 n+2 行 n 個整數:qi。表示 i 號元件直接充電的概率爲 qi%。
Output
輸出一行一個實數,爲進入充電狀態的元件個數的期望,四捨五入到六位小數
題解:
樹形dp+概率dp
啊下面這句話明明就有歧義啊
隨後電能可以從直接充電的元件經過通電的導線使得其他充電元件進行間接充電。
題目又有說自己直接進行充電有個概率。還以爲只有自己能充電的才能通過導線幫別人充電。而實際上,自己是別人通過導線把電能傳給你讓你也能充電的也可以幫別人充電..(有點繞?
那麼對於一個點來說,所有與它相連的都可能影響它。而這是一棵樹,那麼我們分成兩個部分,一個是兒子影響你的,一個是父親影響你的。
設f[i]表示i的兒子不能給i充電的概率,g[i]表示i的父親不能給i充電的概率。
那麼答案就等於
[q[i]是給出的i能自己充電的概率
於是我們需要dfs兩遍來分別求f和g。因爲一個是由上至下一個是由下至上的。
然後我就不知道該說什麼了。
f,g就是根據含義來求啊。
方程什麼的還是要自己推的好。
如果還不會就看代碼吧。不過可能我的式子比較複雜?
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 500010
const double eps=1e-6;
double q[maxn],f[maxn],g[maxn];
//f[i] 兒子不能給i充到電的概率
//g[i] 父親不能給i充到電的概率
struct node
{
int x,y,next;double p;
}a[maxn*2];int len,first[maxn];
void ins(int x,int y,double p)
{
len++;a[len].x=x;a[len].y=y;
a[len].p=p;a[len].next=first[x];first[x]=len;
}
void trp1(int x,int fa)
{
for (int k=first[x];k!=-1;k=a[k].next)
{
int y=a[k].y;
if (y==fa) continue;
trp1(y,x);
double ls=(1-(1-q[y])*f[y])*a[k].p;
//(1-q[y])*f[y]就表示y不能充電的概率
//ls表示y能爲x充電的概率
f[x]*=1-ls;
}
}
void trp2(int x,int fa)
{
for (int k=first[x];k!=-1;k=a[k].next)
{
int y=a[k].y;
if (y==fa) continue;
double ls=(1-(1-q[y])*f[y])*a[k].p;
//ls表示y能爲x充電的概率
if (1-ls>eps) ls=f[x]/(1-ls);
//這裏ls就表示除了y這個孩子外,x的其他孩子不能給x充電的概率
g[y]*=1-a[k].p*(1-(1-q[x])*g[x]*ls);
//(1-q[x])*g[x]*ls就表示x不能充電的概率
trp2(y,x);
}
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int n,x,y,i;double p,ans=0;
scanf("%d",&n);
len=0;memset(first,-1,sizeof(first));
for (i=1;i<n;i++)
{
scanf("%d%d%lf",&x,&y,&p);
p/=100.0;ins(x,y,p);ins(y,x,p);
}
for (i=1;i<=n;i++)
{
scanf("%lf",&q[i]);
q[i]/=100.0;g[i]=f[i]=1.0;
}
trp1(1,0);trp2(1,0);
for (i=1;i<=n;i++)
ans+=1-(1-q[i])*f[i]*g[i];
printf("%.6lf\n",ans);
return 0;
}