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

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=3566
【题解】
  套路题O(n)求出一个的解,然后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-maxans);
    return 0;
}
原文地址:https://www.cnblogs.com/Vanisher/p/9135965.html