【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;
}
posted @ 2017-06-02 14:53  Maxwei_wzj  阅读(106)  评论(0编辑  收藏  举报