【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;//统计答案并输出
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3566.html