[bzoj3566][SHOI2014]概率充电器

题目大意

N个充电器连成一棵树。
第i个充电器有p[i]的概率直接充电。
每条导线有一定几率可以导电。
可以导电的导线形成的联通块中只要存在直接充电的结点整个联通块的充电器均进入充电状态。
问期望进入充电状态的充电器个数

转化为有根树

显然可知我们只需要得到f[i]表示i进入充电状态的概率
那么 ans=f[i]
我们把无根树变有根树。
a[i]=P(ii)+P(ii)P(ii)
即a[i]表示i的父亲不因为i而进入充电状态的概率。
我们设 g[i]=P(ii)

g[i]=P(i)+[1P(i)]P(i)
g[i]=p[i]+(1p[i])(1P(i))
g[i]=p[i]+1p[i](1p[i])jiP(jjj)
g[i]=1(1p[i])jia[j]
现在我们有了g我们需要算出f,不如再设一个辅助数组b。
b[i]=P(ii)
相当于删去i与其父亲的连边后的f’[i的父亲]。
我们知道
g[i]=1(1p[i])jia[j]
1g[i]=(1p[i])jia[j]
那么如果设 x=11g[i]a[j]
那x表示的就是删除i与j的连边后按照原来方法计算出的g’[i]
那么 b[i]=f[i]=g[i]+(1g[i])P(iii)P(ii)
b[i]=11g[i]a[i]+1g[i]a[i]b[i]P(ii)
有了b就可以计算f了
f[i]=g[i]+(1g[i])b[i]P(ii)
两遍dfs计算即可。
不过这题会爆栈!只能打人工栈……
注意一些小细节:比如分母为0直接赋值为0……

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef double db;
const int maxn=500000+10;
int h[maxn],now[maxn],go[maxn*2],next[maxn*2];
db dis[maxn*2];
db a[maxn],b[maxn],g[maxn],f[maxn],p[maxn],q[maxn];
struct dong{
    int x,y;
};
dong sta[maxn],zlt;
int i,j,k,l,t,n,m,tot,top;
db ans;
void add(int x,int y,int z){
    go[++tot]=y;
    dis[tot]=(db)z/100;
    next[tot]=h[x];
    h[x]=tot;
}
void dfs(){
    int t,x,y;
    db l;
    while (top){
        x=sta[top].x;y=sta[top].y;
        t=now[x];
        if (go[t]==y) t=next[t],now[x]=t;
        if (!t){
            l=1;
            t=h[x];
            while (t){
                if (go[t]!=y) l*=a[go[t]];
                t=next[t];
            }
            g[x]=1-(1-p[x])*l;
            a[x]=1-q[x]+q[x]*(1-g[x]);
            top--;
            continue;
        }
        q[go[t]]=dis[t];
        zlt.x=go[t];zlt.y=x;
        sta[++top]=zlt;
        now[x]=next[t];
    }
}
void dg(){
    int t,x,y;
    while (top){
        x=sta[top].x;y=sta[top].y;
        if (!now[x]){
            top--;
            continue;
        }
        if (y&&a[x]) b[x]=1-(1-g[y])/a[x]+(1-g[y])/a[x]*q[y]*b[y];else b[x]=0;
        f[x]=g[x]+(1-g[x])*q[x]*b[x];
        t=h[x];
        now[x]=0;
        while (t){
            if (go[t]!=y){
                zlt.x=go[t];zlt.y=x;
                sta[++top]=zlt;
            }
            t=next[t];
        }
    }
}
int main(){
    freopen("charger.in","r",stdin);freopen("charger.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1){
        scanf("%d%d%d",&j,&k,&l);
        add(j,k,l);add(k,j,l);
    }
    fo(i,1,n){
        scanf("%d",&t);
        p[i]=(db)t/100;
    }
    fo(i,1,n) now[i]=h[i];
    zlt.x=1;zlt.y=0;
    sta[top=1]=zlt;
    dfs();  
    fo(i,1,n) now[i]=1;
    zlt.x=1;zlt.y=0;
    sta[top=1]=zlt;
    dg();
    fo(i,1,n) ans+=f[i];
    printf("%.6lf\n",ans);
}

你可能感兴趣的:([bzoj3566][SHOI2014]概率充电器)