题解 CF123E 【Maze】
题目大意:
给定一个迷宫,这个迷宫图是一个树,每个点都有可能作为进入点和离开点。要求求出:从每个点出发到达别的点的期望。
输入格式: 第一行一个数 n,表示个节点。 接下来 n-1 行,每行两个数(u,v),表示有一条从 u,到v的无向边。 然后是n行,每行描述一个节点表示该节点被选为进入点的 x 和该节点被选为离开的 y 。
此处的x和y用于求每个点被选为进入点的概率和被选为离开点的概率,即: p[x]=x/sum(x), p[y]=y/sum(y).
输出格式:一个实数表示期望。
解法: 这是一道求期望的题。
期望表示某个事件的结果的平均大小。 等于每种结果的大小与其概率乘积的和。
因此,对于这道题,我们要求出每个子树的大小, 和对于每个点:其被选为离开点时,从别的子树出发到达它的概率。
这样我们就可以先统计进入的概率,然后用dfs求出每棵子树的大小,和这个子树中,每个点被选为进入点的概率之和。然后枚举每个点作为离开点,然后求出: 1.从以该节点为根节点的子树出发到该节点的期望。 2.从别的子树出发到该节点的期望。
设该节点编号为 u, 因为对于每个“别的子树”,我们可以将它们作为一个整体,看成u节点的另一个子树。所以我们可以直接用 (1-ru[u])表示将它们选为出发点的概率。用(n-size[u])表示它们的大小。
最后统计答案即可。
贴代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<algorithm>
#include<cstring>
#include<vector>
#include<ctime>
#include<map>
#include<cstdlib>
#include<cmath>
#define p puts("-1");
#define r(i,a,b) for(int i=a;i<=b;i++)
#define rr(i,a,b) for(int i=a;i>=b;i--)
#define inf 0x3f3f3f3f
#define mem(a) memset(a,0x3f,sizeof(a))
#define re(a) a=read()
#define pr(a) printf("%d\n",a)
#define me(a) memset(a,0,sizeof(a))
#define in inline
#define db double
#define red(a) a=readd()
using namespace std;
const int N=100007;
inline int read(){
char ch=getchar();
int w=1,x=0;
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}
return x*w;
}//两种不同类型的数的快读
inline db readd(){
char ch=getchar();
db w=1,x=0;
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0',ch=getchar();}
return x*w;
}
int n,cnt=0,head[N],size[N],fa[N];
double ru[N],chu[N],ans=0,sumru,sumchu;
struct edge{
int v,next;
}e[(N<<1)];
in void add(int u,int v){
e[++cnt]=(edge){
v,head[u]};//数组模拟建边
head[u]=cnt;
}
in void dfs(int u){
size[u]=1;//size[]的初始化
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(v!=fa[u]){
//不用vis[],也可以做到每个点只便利一遍
fa[v]=u;
dfs(v);
size[u]+=size[v];//统计每棵子树的大小
ru[u]+=ru[v];//统计每棵子树被选为进入点的概率之和
}
}
}
int main(){
re(n);
me(head),me(size);//初始化
r(i,1,n-1){
int u,v;
re(u);re(v);
add(u,v);add(v,u);//建边。注意这是无向图
}
r(i,1,n){
red(ru[i]);red(chu[i]);
sumru+=ru[i];sumchu+=chu[i];
}
r(i,1,n)ru[i]/=sumru;//求出每个点被选为进入点的概率:ru[i]=ru[i]/sumru
dfs(1);
r(u,1,n){
//遍历每个点,作为离开点
for(int j=head[u];j;j=e[j].next){
int v=e[j].v;
//统计期望
if(fa[u]==v)ans+=(1-ru[u])*(n-size[u])*chu[u]/sumchu;
//当前的 v 不是u的子节点,此时统计上述的"别的子树"的期望 。
//此时概率等于总概率减去这棵树的概率。
else ans+=ru[v]*size[v]*chu[u]/sumchu;
//统计以u作为根节点的子树的期望
}
}
printf("%.11lf\n",ans);
return 0;
}