题解 P2081 【[NOI2012]迷失游乐园】

· · 题解

题解:考虑只有一棵树,那么down[x]和up[x]分别表示向上走和向下走的期望,然后先dfs一遍求down,down[i]=sigma(down[j]+len[i][j])/dui,也就是期望加起来再加上边的长度再除以总情况数。

然后再求up[x]。up[x]=len[fa[x]][x]+(up[fa[x]]+down[fa[x]]du[fa[x]]-down[x]-len[fa[x]][x])/du[fa[x]]。首先我们要走x到父亲的一段,然后再加上父亲的up+父亲的down父亲的度数,但是这样会把x子树算上,所以我们要减去down[x]和len[fa[x]][x],最后这个整体再除以总数(也就是乘概率),就是up[x]。

那么最后所有节点的ans除以自己的度数(也就是乘概率),再全部加起来除以n,就是最后的答案了。

但是我们有一个环该怎么办呢。

首先这个环导致树变成了len(环)个小树+一个大环,所以我们可以直接对所有小树求down,up就比较麻烦了,我们先对环求出来g数组,表示从这个点开始走的期望,因为环的大小很小,所以我们枚举rt,然后直接dp一遍,g[i]=(g[to[x]]+len[to[x]][x])/(du[x]+1),算完之后,我们再把down计入到贡献中,直接加权即可,但是要注意度数为0的情况,要强制置1。如果下一步就走到了rt,那么我们就不要把g计算到贡献中,因为我们钦定了rt,不能出现走回去的情况;如果当前点就是rt,那么不要计算down的贡献直接return,因为现在我们直接就钦定了up是往rt走的。

算完g和down对答案的贡献之后,我们把环上所有的du都+=2,因为环上有两个出边(前面不加是因为计算的是down的贡献,而g不需要度数来计算),然后我们枚举环上每个点,进行up的计算。

具体看代码吧。

自带大常数(逃

#include <bits/stdc++.h>  
using namespace std;  
struct nod{int to;double len;};  
vector<nod>e[100005];  
int x,y,z,n,m,xi,yi,wi,cir[100005],vis[100005],fa[100005],du[100005],rt,tim;  
double f[100005],d[100005],tp[100005],g[100005],ans;  
void dfs1(int x)  
{  
    vis[x]=1;  
    for(int i=0;i<e[x].size();i++)  
        if(!vis[e[x][i].to]&&!cir[e[x][i].to])  
            dfs1(e[x][i].to),du[x]++,d[x]+=f[e[x][i].to]+e[x][i].len;  
    if(du[x]) f[x]=d[x]/(double)du[x];  
    if(x!=rt) du[x]++;  
}  
void dfs2(int x)  
{  
    vis[x]=1;  
    for(int i=0;i<e[x].size();i++)  
        if(!vis[e[x][i].to]&&!cir[e[x][i].to])  
            d[e[x][i].to]+=(d[x]-f[e[x][i].to]-e[x][i].len)/max(1,du[x]-1)+e[x][i].len,dfs2(e[x][i].to);  
}  
void dfs3(int x)  
{  
    vis[x]=++tim;  
    for(int i=0,j;i<e[x].size();i++)  
        if(e[x][i].to!=fa[x])  
        {  
            if(!vis[e[x][i].to]) fa[e[x][i].to]=x,dfs3(e[x][i].to);  
            else if(vis[e[x][i].to]<vis[x]) for(cir[e[x][i].to]=1,j=x;j!=e[x][i].to;j=fa[j]) cir[j]=1;  
        }  
}  
void dfs4(int x,int fa)  
{  
    bool flag=0;g[x]=0;  
    for(int i=0;i<e[x].size();i++)  
        if(e[x][i].to!=rt&&e[x][i].to!=fa&&cir[e[x][i].to])  
            flag=1,dfs4(e[x][i].to,x),g[x]+=g[e[x][i].to]+e[x][i].len;  
    if(x==rt) return;  
    int k=du[x];k?k:k++;  
    if(!flag) g[x]=d[x]/(double)k;  
    else k=du[x]+1,g[x]=(g[x]+d[x])/(double)k;  
}  
int main()  
{  
    scanf("%d%d",&n,&m);  
    for(int i=1;i<=m;i++) scanf("%d%d%d",&x,&y,&z),e[x].push_back((nod){y,z}),e[y].push_back((nod){x,z});  
    if(m==n-1) rt=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]) rt=i,dfs1(i);  
        for(int i=1;i<=n;i++) if(cir[i]) rt=i,dfs4(i,0),tp[i]=g[i];  
        memset(vis,0,sizeof(vis));  
        for(int i=1;i<=n;i++) if(cir[i]) du[i]+=2,d[i]+=tp[i];  
        for(int i=1;i<=n;i++) if(cir[i]) rt=i,dfs2(i);  
    }  
    for(int i=1;i<=n;i++) ans+=d[i]/(double)du[i];  
    printf("%.5lf\n",ans/(double)n);  
}