zoukankan      html  css  js  c++  java
  • 【BZOJ3566】[SHOI2014] 概率充电器(树形DP)

    点此看题面

    大致题意: 有一棵树,其中每条边有一定的概率存在。每个点有一定概率被直接标记,与被标记的点连通的点也会被标记。求被标记的点数的期望。

    前言

    (Jan 28th)刷题计划(2/6),算法标签:树、DP、概率论。

    挺简单的一道(DP)题吧。

    转化

    点数的期望其实可以分到每个点上,就相当于每个点的期望之和。

    由于一个点的贡献是(1),而期望=概率*贡献,所以此处点数的期望就相当于每个点被标记概率之和。

    那么,我们只要求出每个点被标记的概率即可。

    定义新运算

    考虑一个问题:如果一个点有(x)的概率通过第一种方式被标记,又有(y)的概率通过第二种方式被标记,求这个点被标记的概率。

    这个概率应该是(x+y-xy)(之所以要减掉(xy),是因为如果通过两种方式都标记了它就会计算重复,故减去)。

    方便后文描述,我们定义,(U(x,y)=x+y-xy)

    再考虑一个问题:如果一个点有(x)的概率通过某种方式被标记,而这个点被标记的总概率是(z),求这个点以其他方式被标记的概率(y)

    因为(z=U(x,y)=x+y-xy),所以变形一下就得到了(y=frac{z-x}{1-x})

    考虑到当(x=1)时,(y)为任何值都能使原式成立,所以此时我们可以随便给它赋个值,姑且令此时(y=1)

    方便后文描述,我们定义,(T(z,x)=egin{cases}1&(x=1)\frac{z-x}{1-x}&(x≠1)end{cases})

    动态规划

    我们设(f_x)(x)通过其子树内的点被标记的概率,(g_x)(x)通过其子树外的点被标记的概率(这里的子树内和子树外都不包括(x)自身)。

    容易得到(f_x)(g_x)的转移方程分别为:

    [f_x=U_{y∈son_x}(U(p_y,f_y) imes P_{edge}) ]

    即将通过每个子树被标记的概率统计起来。

    [y∈son_x,g_y=U(U(p_x,g_x),T(f_x,U(p_y,f_y) imes P_{edge})) imes P_{edge} ]

    其中(U(p_x,g_x))(x)通过自身及子树外节点被标记的概率,(T(f_x,U(p_y,f_y) imes P_{edge}))(x)通过除(y)的子树以外其余子树被标记的概率。

    应该还是比较好理解的。

    最后每个节点被标记的概率就是:

    [U(p_x,U(f_x,g_x)) ]

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define DB double
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].p=z/100.0)
    #define U(x,y) ((x)+(y)-(x)*(y))
    #define T(z,x) ((x)==1?1:((z)-(x))/((1)-(x)))
    using namespace std;
    int n,ee,lnk[N+5];DB p[N+5],f[N+5],g[N+5];struct edge {int to,nxt;DB p;}e[N<<1];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    }F;
    I void DP1(CI x,CI lst=0)//第一个树形DP,转移f
    {
    	for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
    		(DP1(e[i].to,x),f[x]=U(f[x],U(p[e[i].to],f[e[i].to])*e[i].p));
    }
    I void DP2(CI x,CI lst=0)//第二个树形DP,转移g
    {
    	for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
    		(g[e[i].to]=U(U(p[x],g[x]),T(f[x],U(p[e[i].to],f[e[i].to])*e[i].p))*e[i].p,DP2(e[i].to,x),0);
    }
    int main()
    {
    	freopen("a.in","r",stdin);
    	RI i,x,y,z;for(F.read(n),i=1;i^n;++i) F.read(x),F.read(y),F.read(z),add(x,y,z),add(y,x,z);
    	for(i=1;i<=n;++i) F.read(z),p[i]=z/100.0;DB ans=0;DP1(1),DP2(1);
    	for(i=1;i<=n;++i) ans+=U(p[i],U(f[i],g[i]));return printf("%.6lf",ans),0;//统计答案并输出
    }
    
  • 相关阅读:
    android的NDK和java进行本地socket通信
    Android 8款开源游戏引擎
    Android使用AndEngine创建第一个程序
    J2ME项目移植到Android平台六大注意事项
    面面具到!android重力传感器
    android应用程序的混淆打包
    Android 5.0最应该实现的8个期望
    android service 的各种用法(IPC、AIDL)
    Android上怎样使用《贝赛尔曲线》
    Java序列化与反序列化学习(二):序列化接口说明
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3566.html
Copyright © 2011-2022 走看看