• [SHOI2014]概率充电器 dp


     

    转载请注明出处:

    http://www.cnblogs.com/hzoi-wangxh/p/7738625.html 

    [SHOI2014]概率充电器

    时间限制: 4 Sec  内存限制: 256 MB

    题目描述

    著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器:
    “采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看吧!

    SHOI 概率充电器由 n-1 条导线连通了 n 个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。
    随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。
    作为 SHOI 公司的忠实客户,你无法抑制自己购买 SHOI 产品的冲动。在排了一个星期的长队之后终于入手了最新型号的 SHOI 概率充电器。
    你迫不及待地将 SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?

    输入

    第一行一个整数:n。概率充电器的充电元件个数。充电元件由 1-n 编号。
    之后的 n-1 行每行三个整数 a, b, p,描述了一根导线连接了编号为 a 和 b 的
    充电元件,通电概率为 p%。
    第 n+2 行 n 个整数:qi。表示 i 号元件直接充电的概率为 qi%。

    输出

    输出一行一个实数,为进入充电状态的元件个数的期望,四舍五入到六位小数

    样例输入

    3 
    1 2 50 
    1 3 50 
    50 0 0 
    

    样例输出

    1.000000
    

    提示

    对于 100%的数据,n≤500000,0≤p,qi≤100。


    题解:

     我们很容易想到用高斯消元去解,f[i]=sigma(f[v]*l[i].p) +f[fa]*l[i].p,列出方程组求解,最后的ans=sigma(f[i])。

    然而这是O(n^3),数据是500000,过不了,于是我们应去想线性的做法。

    跑两遍dfs,第一遍处理出把这个点当作根时的f[i]。由容斥可得,f[i]=q[i]+sigma(f[v]*l[i].p-f[i]*f[v]*l[i].p),加上由v转移过来使它发光的概率,减去原本两灯同时发光的概率。

    第二遍dfs是由它的father转移过来。首先它的father应把它的贡献删去,即把第一遍的乘法逆过来,就是P=(f[fa]-l[i].p*fa[x])/(1-l[i].p*fa[x])。之后仍然由容斥得,f[i]=f[i]+(1-fa[i])*P*l[i].p。

    最后ans=sigma(f[i]);

    附上代码:

     

    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    struct tree{
    	int u,v,next;
    	double d;
    }l[1001000];
    double f[501000],ans,ss[501000],a[501000],eps=1e-8;
    int e,n,lian[501000],fa[501000];
    void bian(int,int,int);
    void dfs1(int);
    void dfs2(int);
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<n;i++)
    	{
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		bian(x,y,z);
    		bian(y,x,z);
    	}
    	for(int i=1;i<=n;i++) scanf("%lf",&a[i]),a[i]=a[i]*0.01;
    	dfs1(1);
    	dfs2(1);
    	for(int i=1;i<=n;i++)
    		ans+=f[i];
    	printf("%0.6lf",ans);
    	return 0;
    }
    void bian(int x,int y,int z)
    {
    	e++;
    	l[e].u=x;
    	l[e].v=y;
    	l[e].d=(double)z*0.01;
    	l[e].next=lian[x];
    	lian[x]=e;
    }
    void dfs1(int x)
    {
    	f[x]=a[x];
    	for(int i=lian[x];i;i=l[i].next)
    	{
    		int v=l[i].v;
    		if(v!=fa[x])
    		{
    			fa[v]=x;
    			dfs1(v);
    			double p=l[i].d*f[v];
    			if(p>eps)
    				f[x]=f[x]+p*(1-f[x]);
    		}
    	}
    }
    void dfs2(int x)
    {
    	for(int i=lian[x];i;i=l[i].next)
    	{
    		int v=l[i].v;
    		if(v!=fa[x])
    		{
    			double p=(f[x]-f[v]*l[i].d)/(1.0-f[v]*l[i].d);
    			if(p>eps)
    				f[v]=f[v]-f[v]*l[i].d*p+l[i].d*p;
    			dfs2(v);
    		}
    	}
    }


  • 相关阅读:
    Linux -- touch
    Linux -- ls
    Linux -- 手动新建用户
    Linux -- id
    Linux -- chfn
    Linux -- finger
    Linux -- newgrp
    浅谈java中线程和操作系统线程
    java虚拟机入门(五)- 常见垃圾回收器及jvm实现
    java虚拟机入门(四)-垃圾回收的故事
  • 原文地址:https://www.cnblogs.com/hzoi-wangxh/p/7738625.html
Copyright © 2020-2023  润新知