SHOI 2014 概率充电器

本来先做的聪聪与可可,然后没做出来去做的4284,然后又看了看换根dp,然后又看了看spfa,我tcl

注意计算概率时需要考虑是否要用容斥原理,期望dp时注意有时要用倒序枚举

P4284概率充电器

我们可以把总的期望个数转化为每个点对答案的贡献,如果某个点被点亮贡献为1,否则为0,所以我们只要求出每个点被点亮的概率即可

\(p[u]\)是点\(u\)亮的概率,一条边能通电概率为\(e[u,v]\)

那么化式子的时候有很多类似\(1-P\)的式子,所以我们设\(p[u]\)是u不亮的概率(正难则反)昂神最喜欢说了:)逃

\(p[u]=(1-q[u])\prod_{(u,v)\in E}(1-e[u,v]+e[u,v][p,v])\)

式子有依赖,giao斯小猿吗,可吧

但选择\(树形DP换根\)就完了,pj神勃客写过qwq,先把无根树随便定根,设\(f[u]为u不被它子树(含自身)的点点亮的概率\)

\(f[u]=(1-q[u])\prod_{v \in son[u]}(1-e[u,v]+e[u,v]f[v])\)

\(跑一遍dfs,定下根的答案,但其他点的答案还不正确,考虑把根换成u\)

\(g[u]\)是以\(u\)为根时\(u\)不被点亮的概率,很显然分为两部分:\(u\)原来的子树,剩下的部分,\(u\)原来的子树我们已经得到了,剩下的部分可以通过\(g[fa]\)去掉u的贡献得到

\(P(rest)=g[fa]/(1-e[fa,u]+e[fa,u]f[u])\)

那么\(g[u]=f[u(l-e[fa,u]+e[fa,u]P(rest))]\)

\(最后u的概率为1-g[u]\)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=2e6;
struct Edge{int v;double w;}e[N];
int mm,n,fst[N],nxt[N];
double p[N],f[N],g[N];
void ade(int u,int v,double w){e[++mm]=(Edge){v,w};nxt[mm]=fst[u],fst[u]=mm;}
void dfs1(int u,int fa)
{
	f[u]=1-p[u];
	for(int i=fst[u];i;i=nxt[i])
	{
		int v=e[i].v;
		if(v==fa)continue;
		dfs1(v,u);
		f[u]*=(1-e[i].w+e[i].w*f[v]);
	}
}
void dfs2(int u,int fa,int ei)
{
	if(u==1)g[u]=f[u];//如果u是根,那么什么都不用管.
	else
	{
		double P=g[fa]/(1-e[ei].w+e[ei].w*f[u]);
		g[u]=f[u]*(1-e[ei].w+e[ei].w*P);
	}
	for(int i=fst[u];i;i=nxt[i])
	{
		int v=e[i].v;
		if(v==fa)continue;
		dfs2(v,u,i);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		ade(u,v,w*0.01),ade(v,u,w*0.01);
	}
	for(int i=1;i<=n;i++)scanf("%lf",p+i),p[i]*=0.01;
	dfs1(1,0),dfs2(1,0,0);double ans=0;
	for(int i=1;i<=n;i++)ans+=1-g[i];
	printf("%.6lf",ans);
}

P4206聪聪与可可

猫可以走一步或两步;老鼠可以不动;猫必须走到离老鼠最近的点,如距离有相同,则选编号最小的点

预处理:预处理猫在\(i\),老鼠在\(j\),的下一个走位\(nxt[i][j]\),\(SPFA预处理出最短路径dis[i][j]\),接下来才能预处理猫的走位

等等我有点乱,先咕着

posted @ 2020-07-19 21:05  INFP  阅读(146)  评论(4编辑  收藏  举报