测试地址:迷失游乐园
做法:这题简直是神题啊……顶礼膜拜orz……
这题需要用到环套树+树形DP+期望DP。
前面具体的分析过程、状态转移方程和我这篇文章用到的符号定义请看这位大佬的题解。
上面这个大佬讲得很详细了,我这里只对求环上点
up
值的过程做一些补充。
求环上的点的
up
值要这样求:首先从一个点可以往两个方向走,每走到一个点
i
都有
其他的在上面大佬的文里都说得很清楚,这里就不赘述了。
以下是本人代码:
#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;
}