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]\),接下来才能预处理猫的走位
等等我有点乱,先咕着