• [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;
    }
    概率充电器
  • 相关阅读:
    win10应用 UWP 使用MD5算法
    win10应用 UWP 使用MD5算法
    用git上传代码到新浪云
    用git上传代码到新浪云
    redis三节点sentinel部署
    [HNOI/AHOI2018]转盘
    用git上传代码到新浪云
    Windows-universal-samples-master示例 XamlCommanding
    Windows-universal-samples-master示例 XamlCommanding
    Windows-universal-samples-master示例 XamlCommanding
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13307502.html
Copyright © 2020-2023  润新知