题解 CF123E 【Maze】

题解 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值