• 【NOI2012T4】迷失游乐园-环套树+树形DP+期望DP


    测试地址:迷失游乐园
    做法:这题简直是神题啊……顶礼膜拜orz……
    这题需要用到环套树+树形DP+期望DP。
    前面具体的分析过程、状态转移方程和我这篇文章用到的符号定义请看这位大佬的题解
    上面这个大佬讲得很详细了,我这里只对求环上点up值的过程做一些补充。
    求环上的点的up值要这样求:首先从一个点可以往两个方向走,每走到一个点i都有son[i]/(son[i]+1)的概率走到它管辖的外向树上,那么我们设从原始点走到当前点的概率为p,那么i管辖的外向树给原始点的up带来的贡献就是p×son[i]×down[i]/(son[i]+1),同时用p我们也可以计算pre[i](点i的上一个点)与i之间的边给原始点的up带来的贡献,显然走过这条边的概率就是p,所以这条边带来的贡献就是p×len(pre[i]>i),把这两个贡献加起来就是上面大佬代码中的的那个式子了。要注意特殊情况:当走到了pre[]时,就不能再走环上的点了,那么下到外向树的概率就是1,而不是son[i]/(son[i]+1)。然后按照顺序一个一个计算即可。
    其他的在上面大佬的文里都说得很清楚,这里就不赘述了。
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #define eps 1e-9
    using namespace std;
    int n,m,tot=0,first[100010]={0},lp[25],lplen;
    double up[100010],down[100010],fa[100010],son[100010],lpd[25];
    struct edge {int v,next;double d;} e[200010];
    bool inlp[100010]={0},vis[100010]={0},flag=0;
    
    void insert(int a,int b,double d)
    {
      e[++tot].v=b,e[tot].d=d,e[tot].next=first[a],first[a]=tot;
    }
    
    bool find_loop(int v,int f)
    {
      vis[v]=1;
      for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=f)
        {
          if (vis[e[i].v])
          {
            lp[1]=e[i].v;
            lp[2]=v;
            lpd[1]=e[i].d;
            inlp[e[i].v]=inlp[v]=1;
            lplen=2;
            flag=1;
            return 1;
          }
          else if (find_loop(e[i].v,v))
          {
            if (v==lp[1]) flag=0;
            if (flag)
            {
              lpd[lplen]=e[i].d;
              lp[++lplen]=v;
              inlp[v]=1;
            }
            return 1;
          }
        }
      return 0;
    }
    
    void calc_down(int v,int f)
    {
      if (!inlp[v]) fa[v]=1;
      down[v]=son[v]=0;
      for(int i=first[v];i;i=e[i].next)
        if (!inlp[e[i].v]&&e[i].v!=f)
        {
          son[v]+=1;
          calc_down(e[i].v,v);
          down[v]+=down[e[i].v]+e[i].d;
        }
      if (fabs(son[v])>eps) down[v]/=son[v];
    }
    
    void calc_up(int v,int f)
    {
      for(int i=first[v];i;i=e[i].next)
        if (!inlp[e[i].v]&&e[i].v!=f)
        {
          int x=e[i].v;
          if (fabs(son[v]-1+fa[v])>eps)
            up[x]=e[i].d+(son[v]*down[v]-down[x]-e[i].d+up[v]*fa[v])/(son[v]-1+fa[v]);
          else up[x]=e[i].d;
          calc_up(e[i].v,v);
        }
    }
    
    int main()
    {
      scanf("%d%d",&n,&m);
      for(int i=1;i<=m;i++)
      {
        int a,b;
        double d;
        scanf("%d%d%lf",&a,&b,&d);
        insert(a,b,d),insert(b,a,d);
      }
    
      if (m==n-1)
      {
        lplen=1;
        inlp[1]=1;
        lp[1]=1;
        fa[1]=up[1]=0;
      }
      else
      {
        find_loop(1,0);
        for(int i=first[lp[lplen]];i;i=e[i].next)
          if (e[i].v==lp[1])
          {
            lpd[lplen]=e[i].d;
            break;
          }
      }
    
      for(int i=1;i<=lplen;i++)
        calc_down(lp[i],0);
      if (lplen>1)
      {
        for(int i=1;i<=lplen;i++)
        {
          fa[lp[i]]=2;
          up[lp[i]]=0;
          int j=i%lplen+1;
          double p=0.5;
          while(j!=i)
          {
            double len=lpd[(j-1)?(j-1):lplen];
            if (j%lplen+1!=i)
              up[lp[i]]+=p*(len+son[lp[j]]*down[lp[j]]/(son[lp[j]]+1));
            else up[lp[i]]+=p*(len+down[lp[j]]);
            p/=(son[lp[j]]+1);
            j=j%lplen+1;
          }
          j=(i-1)?(i-1):lplen,p=0.5;
          while(j!=i)
          {
            if (((j-1)?(j-1):lplen)!=i)
              up[lp[i]]+=p*(lpd[j]+son[lp[j]]*down[lp[j]]/(son[lp[j]]+1));
            else up[lp[i]]+=p*(lpd[j]+down[lp[j]]);
            p/=(son[lp[j]]+1);
            j=(j-1)?(j-1):lplen;
          }
        }
      }
      for(int i=1;i<=lplen;i++)
        calc_up(lp[i],0);
    
      double ans=0;
      for(int i=1;i<=n;i++)
        ans+=(son[i]*down[i]+up[i]*fa[i])/(son[i]+fa[i]);
      ans/=n;
      printf("%.5lf",ans);
    
      return 0;
    }
    
  • 相关阅读:
    关于linux命令 cp,rm,mv执行时是否询问的问题,不同用户有不同的别名设置
    用jquery根据json动态创建多级菜单导航(by https://www.cnblogs.com/fatty-yu/p/7088955.html)
    前台向后台传值的两种方法 以及 从后台获取数据的方法
    “加载更多”功能如何实现
    AngularJS的主要组成部分:
    数组与字符串的相互转化
    angular过滤器的使用 By http://www.tuicool.com/articles/ueUZBv
    transclude
    内嵌模版 template 原创
    导航栏选中效果 ng-repeat
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793662.html
Copyright © 2020-2023  润新知