[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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值