P4284 [SHOI2014]概率充电器

P4284 [SHOI2014]概率充电器


今天上课讲到的题orz,第一次做这种上下搞两次dp的题。

g[i]表示i的子树(包括i)给i充电的概率。

f[i]表示i的父亲给i充电的概率。

g[]可以快速求出来。h[i]表示i对i父亲的贡献,则

\(h_i=g_i+(1-g_i)(1-w(i,fa_i))\)

\(g_i=(1-q_i)\Pi_{j=son}h_j\)

\(w(i,fa_i)\)\(i\)\(fa_i\)之间路径的长度。

但是f[]就不好求了= =

上面已经求出了f[y],要求f[x]。f[x]需要计算除了x的子树之外的所有点。

可以把这些点分成两部分:绿色和紫色部分

紫色部分就是f[y],但绿色呢?

回去看一眼:\(g_y=(1-q_y)\Pi_{j=son}h_j\)

所以只需要除掉\(h_x\)就星了。

\(t=\frac{g_yf_y}{h_x}\)

\(f_x=t+(1-t)(1-w(x,y))\)

注意t不是\(f_x\),t是去掉x子树后y通电的概率,可参照h的求法


上述方法可获得95分的好成绩。

因为\(t=\frac{g_yf_y}{h_x}\),如果遇到0就GG了。。。

观察一下,\(h_x=0\)的话,t实际是0(因为上面一定会通电下来??

然后写完了= =

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cctype>
using namespace std;
typedef int mmp;
#define vd void
#define rg register
#define il inline
#define sta static
il int gi(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=500001;
int id,fir[maxn],nxt[maxn<<1],dis[maxn<<1],w[maxn<<1];
int p[maxn];
il vd link(int x,int y,int z){nxt[++id]=fir[x],fir[x]=id,dis[id]=y,w[id]=z;}
double f[maxn],g[maxn],h[maxn];
il vd dp(int x,int fa=-1){
    g[x]=1-p[x]*.01;
    for(rg int i=fir[x];i;i=nxt[i]){
        if(fa==dis[i])continue;
        dp(dis[i],x);
        h[dis[i]]=g[dis[i]]+(1-g[dis[i]])*(1-w[i]*.01);
        g[x]*=h[dis[i]];
    }
}
il vd dp2(int x,int fa=-1){
    for(rg int i=fir[x];i;i=nxt[i]){
        if(fa==dis[i])continue;
        double t=(h[dis[i]]<1e-6)?0:g[x]*f[x]/h[dis[i]];
        f[dis[i]]=t+(1-t)*(1-w[i]*.01);
        dp2(dis[i],x);
    }
}
mmp main(){
// 	freopen("4284.in","r",stdin);
// 	freopen("4284.out","w",stdout);
    int n=gi();
    for(rg int i=1;i<n;++i){
        sta int a,b,p;
        a=gi(),b=gi(),p=gi();
        link(a,b,p),link(b,a,p);
    }
    for(rg int i=1;i<=n;++i)p[i]=gi();
    dp(1);f[1]=1;dp2(1);
    double ans=0;
    for(rg int i=1;i<=n;++i)ans+=1-f[i]*g[i];
    printf("%.6lf\n",ans);
// 	for(rg int i=1;i<=n;++i)printf("%.6lf ",f[i]);puts("");
// 	for(rg int i=1;i<=n;++i)printf("%.6lf ",g[i]);puts("");
// 	for(rg int i=1;i<=n;++i)printf("%.6lf ",h[i]);puts("");
    return 0;
}
posted @ 2018-03-27 17:15  菜狗xzz  阅读(164)  评论(0编辑  收藏  举报