zoukankan      html  css  js  c++  java
  • [SHOI2014] 概率充电器

    题意:

    给定一棵n个点的树,每个点有$q_u$的概率亮,每条边有$p_{u,v}$的概率存在。

    如果一个连通块里有亮的点那么这个连通块里所有点都变成亮的。

    问期望有多少个点是亮的。

    $nleq 500000$。

    题解:

    一开始按照传统dp那样推了……其实树上期望的题一般都不是传统题。

    用期望的线性性拆一下,其实就是求每个点被点亮的概率之和。

    考虑设$f_u$表示u被点亮的概率,但这东西有点难求,需要容斥。

    于是直接设$f_u$表示u没被点亮的概率,那么有$f_u = (1- q_u )prod f_v + p_{u,v}(1-f_v )$。

    发现如果直接树形dp的话父子时间会互相影响,这数据范围也做不了高斯消元。

    于是考虑换根dp,直接dfs一遍算出点1的答案,然后再dfs一遍往下算答案即可。

    复杂度$O(n)$。

    套路:

    • 较复杂的期望dp$ ightarrow$先用线性性拆一下。
    • 求期望合法的个数$ ightarrow$求每个合法的概率之和。
    • 期望的本质就是算概率。
    • 换根dp$ ightarrow$先dfs一遍算出根的答案,再把每个点的原答案加上父亲的答案去掉它的贡献。

    代码:

    #include<bits/stdc++.h>
    #define maxn 500005
    #define maxm 500005
    #define inf 0x7fffffff
    #define ll long long
    #define rint register int
    #define debug(x) cerr<<#x<<": "<<x<<endl
    #define fgx cerr<<"--------------"<<endl
    #define dgx cerr<<"=============="<<endl
    
    using namespace std;
    int n,hd[maxn],to[maxn<<1],nxt[maxn<<1],cnt;
    double P[maxn<<1],Q[maxn],F[maxn],G[maxn];
    
    inline int read(){
        int x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    
    inline void addedge(int u,int v,double p){
        to[++cnt]=v,P[cnt]=p,nxt[cnt]=hd[u],hd[u]=cnt;
        to[++cnt]=u,P[cnt]=p,nxt[cnt]=hd[v],hd[v]=cnt;
    }
    
    inline void dfs1(int u,int fa){
        F[u]=1.0-Q[u];
        for(int i=hd[u];i;i=nxt[i]){
            int v=to[i]; double p=P[i];
            if(v==fa) continue; dfs1(v,u);
            F[u]*=(1.0+p*(F[v]-1.0));
        }
    }
    inline void dfs2(int u,int fa){
        if(u==1) G[u]=F[u];
        for(int i=hd[u];i;i=nxt[i]){
            int v=to[i]; double p=P[i];
            if(v==fa) continue;
            if((1.0+p*(F[v]-1.0)==0)){cout<<n<<".000000"<<endl,exit(0);}
            double tp=G[u]/(1.0+p*(F[v]-1.0));
            G[v]=F[v]*(1.0+p*(tp-1.0)),dfs2(v,u);
        }
    }
    
    int main(){
        n=read();
        for(int i=1;i<n;i++){
            int u=read(),v=read(),p=read();
            addedge(u,v,p/100.0);
        }
        for(int i=1;i<=n;i++) Q[i]=read()/100.0;
        dfs1(1,0),dfs2(1,0);
        double ans=0;
        for(int i=1;i<=n;i++) ans+=1.0-G[i];
        printf("%.6lf
    ",ans);
        return 0;
    }
    概率充电器
  • 相关阅读:
    POJ 3660 Cow Contest (floyd求联通关系)
    POJ 3660 Cow Contest (最短路dijkstra)
    POJ 1860 Currency Exchange (bellman-ford判负环)
    POJ 3268 Silver Cow Party (最短路dijkstra)
    POJ 1679 The Unique MST (最小生成树)
    POJ 3026 Borg Maze (最小生成树)
    HDU 4891 The Great Pan (模拟)
    HDU 4950 Monster (水题)
    URAL 2040 Palindromes and Super Abilities 2 (回文自动机)
    URAL 2037 Richness of binary words (回文子串,找规律)
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13307502.html
Copyright © 2011-2022 走看看