[bzoj3566][SHOI2014]概率充电器【树形dp】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=3566
【题解】
  套路题 O(n) O ( n ) 求出一个的解,然后 O(1) O ( 1 ) 换根。
  *注意除0的问题。

/* --------------
    user Vanisher
    problem bzoj-3566
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       500010
using namespace std;
int read(){
    int tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}
struct node{
    int data,next;
    long double vote; 
}e[N*2];
int place,head[N],n,st[N*5],id,cnt[N];
void build(int u, int v,long double w){
    e[++place].data=v; e[place].next=head[u]; head[u]=place; e[place].vote=w;
    e[++place].data=u; e[place].next=head[v]; head[v]=place; e[place].vote=w; 
}
long double p[N],ans[N],now[N],rd[N*5];
long double solve(int x, int fa){
    now[x]=0;
    for (int ed=head[x]; ed!=0; ed=e[ed].next)
        if (e[ed].data!=fa){
            double nex=(p[e[ed].data]+(1-p[e[ed].data])*solve(e[ed].data,x))*e[ed].vote;
            if (cnt[e[ed].data]!=0) nex=e[ed].vote; 
            if (nex==1) cnt[x]++;
                else now[x]=now[x]+(1-now[x])*nex;
        }
    return now[x];
}
void dfs(int x, int fa){
    st[++id]=x;
    for (int ed=head[x]; ed!=0; ed=e[ed].next)
        if (e[ed].data!=fa){
            rd[id]=e[ed].vote;
            dfs(e[ed].data,x);
            st[++id]=x;
            rd[id-1]=e[ed].vote;
        }
}
int main(){
    n=read();
    for (int i=1; i<n; i++){
        int u=read(), v=read();
        long double w=read()/100.0;
        build(u,v,w);
    }
    for (int i=1; i<=n; i++)
        p[i]=read()/100.0;
    solve(1,0);
    dfs(1,0);
    for (int i=1; i<=id; i++){
        if (cnt[st[i]]>0)
            ans[st[i]]=0;
            else ans[st[i]]=1-(p[st[i]]+(1-p[st[i]])*now[st[i]]);
        if (i<id){
            int x=st[i], y=st[i+1];
            double tmp=(p[y]+(1-p[y])*now[y])*rd[i];
            if (tmp==1||(cnt[y]>0&&rd[i]==1)) cnt[x]--;
                else now[x]=(now[x]-tmp)/(1-tmp);
            tmp=(p[x]+(1-p[x])*now[x])*rd[i];
            if (tmp==1||(cnt[x]>0&&rd[i]==1)) cnt[y]++;
                else now[y]=now[y]+(1-now[y])*tmp;
        }
    }
    long double maxans=0;
    for (int i=1; i<=n; i++)
        maxans=maxans+ans[i];
    printf("%.6Lf\n",n-maxans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值