CF123E Maze(期望dp,树形dp,式子)

题目大意:
给你一棵树,边权都是1,每一个点有一个是起点的概率和一个是终点的概率,你将以起点为根,开始在树上随机dfs,每到一个点,就会将他的所有儿子随机打乱成序列,然后按照那个随机顺序走完,直到走到终点。求dfs从起点到终点的期望长度。

其实一开始看到这个题,还是有点懵逼的啊

根据期望的线性性,我们可以通过求所有相邻点的期望,然后直接相加,得到ans

那我们可以这么考虑,对于一个点来说,假设我们要求的是从\(x->y\)\(y\)\(x\)的儿子)的期望的话,如果走到一个错的儿子,那么就需要\(2*size[son]\)步重新回来(相当于每条边会走两遍),而每个儿子在对应的\(y\)之前的概率又都是\(\frac{1}{2}\)(所有排列中,要不是y在前,就是那一个在前)

所以可以得知,每一个点的贡献 就是\(size[x]\)

那么我们可以通过枚举终点,然后算其他子树的\(size[x]\)以及概率和,求出来他们的贡献

最后直接输出ans就好

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 2e5+1e2;
const int maxm = 2*maxn;

int point[maxn],nxt[maxm],to[maxm];
double val[maxn];
int size[maxn];
double st[maxn],ed[maxn];
int n,m;
double sumst,sumed;
int cnt;
double ans;

void addedge(int x,int y)
{
    nxt[++cnt]=point[x];
    to[cnt]=y;
    point[x]=cnt;
}

void dfs(int x,int fa)
{
    size[x]=1;
    val[x]=st[x];
    //cout<<x<<" "<<val[x]<<endl;
    for (int i=point[x];i;i=nxt[i])
    {
       int p = to[i];
       if (p==fa) continue;
       dfs(p,x);
       size[x]+=size[p];
       val[x]+=val[p];
    }
} 
int main()
{
  n=read();
  for (int i=1;i<n;i++)
  {
    int x=read(),y=read();
    addedge(x,y);
    addedge(y,x);
  }
  
  for (int i=1;i<=n;i++)
  {
     scanf("%lf%lf",&st[i],&ed[i]);
     sumst+=st[i];
     sumed+=ed[i];
  }
  
  for(int i=1;i<=n;i++)
  {
      st[i]=st[i]/sumst;
      ed[i]=ed[i]/sumed;
     // printf("%.4lf %.4lf\n",st[i],ed[i]);
  }
  
  dfs(1,0);
  //cout<<val[1]<<endl;
  for (int x=1;x<=n;x++)
  {
     for  (int i=point[x];i;i=nxt[i])
     {
        int p =to[i];
        if (size[p]>=size[x])
          ans=ans+1.0*(1.0-val[x])*1.0*(1.0*n-size[x])*ed[x];
        else
          ans=ans+val[p]*1.0*size[p]*ed[x];
          
    //  cout<<x<<" "<<p<<" "<<ans<<endl; 
     }
  } 
  
  printf("%.12lf",ans);
  return 0;
}

转载于:https://www.cnblogs.com/yimmortal/p/10161463.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值