1. 程式人生 > >BZOJ 3566 概率充電器

BZOJ 3566 概率充電器

har spa () clas ati \n include pri ++

先假設只能向上導電,這樣我們可以DFS一遍得到樹根帶電的概率,得到 \(O(n^2)\) 做法

對於非樹根點,考慮一下(父子間)變化量,O(n)

註意不要除0,將要除0時特殊處理一下

#include <cstdio>
#include <cctype>

const double eps=1e-10;
const int MAXN=500111;

char gc(){
    static char buf[1<<16], *s=buf, *t=buf;
    if(s==t)    t=(s=buf)+fread(buf, 1, 1<<16, stdin);
    if(s==t)    return EOF;
    return *s++;
}

int read(){
    int x=0, f=1;char ch=gc();
    while(!isdigit(ch)) {if(ch=='-')f=-f;ch=gc();}
    while(isdigit(ch))  {x=x*10+(ch-'0');ch=gc();}
    return x*f;
}

int N;

struct Vert{
    int FE;
    double p, f, t, s;
    double cal(double k){
        return p+(1.0-p)*(1.0-k);
    }
} V[MAXN];

struct Edge{
    int y, next;
    double p;
} E[MAXN<<1];

int Ecnt;

void addE(int a, int b, double c){
    ++Ecnt;
    E[Ecnt].y=b;E[Ecnt].next=V[a].FE;V[a].FE=Ecnt;E[Ecnt].p=c;
}

void DFS(int at, int f){
    V[at].t=1.0;
    for(int k=V[at].FE, to;k;k=E[k].next){
        to=E[k].y;
        if(to==f)   continue;
        DFS(to, at);
        V[at].t*=1.0-E[k].p*V[to].f;
    }
    V[at].f=V[at].cal(V[at].t);
}

void DFS(int at, double pf, int f){
    V[at].t*=(1.0-pf);
    V[at].s=V[at].cal(V[at].t);
    for(int k=V[at].FE, to;k;k=E[k].next){
        to=E[k].y;
        if(to==f)   continue;
        DFS(to, (1.0-V[to].f<eps)?0.0:E[k].p*V[at].cal(V[at].t/(1.0-E[k].p*V[to].f)), at);
    }
}

int main(){
    
    N=read();
    
    for(int i=1, a, b, c;i<N;++i){
        a=read();b=read();c=read();
        addE(a, b, (double)(c)/100.0);
        addE(b, a, (double)(c)/100.0);
    }
    
    for(int i=1;i<=N;++i)   V[i].p=(double)(read())/(100.0);
    
    DFS(1, 0);
    DFS(1, 0.0, 0);
    
    //for(int i=1;i<=N;++i) printf("%lf ", V[i].s);
    //puts("");
    double Ans=0.0;
    for(int i=1;i<=N;++i)   Ans+=V[i].s;
    printf("%.6lf\n", Ans);
    
    return 0;
}

/*
3
1 2 50
1 3 50
50 0 0

*/

BZOJ 3566 概率充電器