[BZOJ2878][NOI2012]迷失游乐园(概率期望dp+图论)

题目:

我是超链接

题解:

感觉相当的复杂。我们先考虑一棵树的情况

首先一个点可以往上走(父节点),可以往下走(子节点),我们就分为这两种情况讨论
一遍dfs可以求出 d o w n [ i ] = ( d o w n [ j ] + l e n ( i , j ) ) / d u [ i ] ,i为j的父节点
再来一遍dfs求出向上的期望值 u p [ j ] = ( d o w n [ i ] l e n ( i , j ) d o w n [ j ] ) / ( d u [ i ] 1 ) + l e n ( i , j ) ,i为j的父节点

但是有环的情况就要特别讨论了
首先这个环导致树变成了len个小树+一个大环,所以我们对所有的小树求down,up比较麻烦,我们先对环上的点求出g[i]表示从这个点开始走的期望,因为环的大小很小,我们枚举root,然后dp,g[i]=(g[v[i]]+len(i,v[i]))/(du[x]+1),算完之后,我们再把down计入贡献直接加权。

算完g和down对答案的贡献之后,我们把环上的du+2,因为环上的两个出边没有算,然后枚举环上每个点,进行up的计算

对于度数为0的情况,要强制置1

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=100005;
int tot,nxt[N*2],point[N],v[N*2],c[N*2],vis[N],du[N],root,num,fa[N],fff;double g[N],f[N],d[N],xin[N];
bool cir[N];
void addline(int x,int y,int z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void dfs1(int x)
{
    vis[x]=1;
    for (int i=point[x];i;i=nxt[i])
      if (!vis[v[i]] && !cir[v[i]])
        dfs1(v[i]),du[x]++,d[x]+=f[v[i]]+c[i];
    if (du[x]) f[x]=d[x]/(double)du[x];
    if (x!=root) du[x]++;
}
void dfs2(int x)
{
    vis[x]=1;
    for (int i=point[x];i;i=nxt[i])
      if (!vis[v[i]] && !cir[v[i]]) d[v[i]]+=(d[x]-f[v[i]]-c[i])/(double)max(1,du[x]-1)+c[i],dfs2(v[i]);
}
void dfs3(int x)
{
    vis[x]=++num;int j;
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa[x])
      {
          if (!vis[v[i]]) fa[v[i]]=x,dfs3(v[i]);
          else if (vis[v[i]]<vis[x])
          {
             for (cir[v[i]]=1,j=x;j!=v[i];j=fa[j]) 
               cir[j]=1;
          } 
      }
}
void dfs4(int x,int fa)
{
    g[x]=0;int fff=1;
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa && cir[v[i]] && v[i]!=root) fff=0,dfs4(v[i],x),g[x]+=g[v[i]]+c[i];
    if (x==root) return;
    int k=du[x];
    if (fff) g[x]=d[x]/(double)max(1,du[x]);
    else g[x]=(g[x]+d[x])/(double)(du[x]+1);
}
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        addline(x,y,z);
    }
    double ans=0;root=1;
    if (m==n-1) dfs1(1),memset(vis,0,sizeof(vis)),dfs2(1);
    else
    {
        dfs3(1);memset(vis,0,sizeof(vis));
        for (int i=1;i<=n;i++) if (cir[i]) root=i,dfs1(i);
        for (int i=1;i<=n;i++) if (cir[i]) root=i,dfs4(i,0),xin[i]=g[i];
        memset(vis,0,sizeof(vis));
        for (int i=1;i<=n;i++) if (cir[i]) du[i]+=2,d[i]+=xin[i];
        for (int i=1;i<=n;i++) if (cir[i]) root=i,dfs2(i);  
    }
    for (int i=1;i<=n;i++) ans+=d[i]/(double)du[i];
    printf("%.5lf",ans/(double)n);
}

猜你喜欢

转载自blog.csdn.net/blue_cuso4/article/details/80499596