2878: [Noi2012]迷失游乐园
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 894 Solved: 512
[Submit][Status][Discuss]
Description
放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩。进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点、m条道路的无向连通图,且该图中至多有一个环(即m只可能等于n或者n-1)。小Z现在所在的大门也正好是一个景点。小Z不知道什么好玩,于是他决定,从当前位置出发,每次随机去一个和当前景点有道路相连的景点,并且同一个景点不去两次(包括起始景点)。贪玩的小Z会一直游玩,直到当前景点的相邻景点都已经访问过为止。小Z所有经过的景点按顺序构成一条非重复路径,他想知道这条路径的期望长度是多少?小Z把游乐园的抽象地图画下来带回了家,可是忘了标哪个点是大门,他只好假设每个景点都可能是大门(即每个景点作为起始点的概率是一样的)。同时,他每次在选择下一个景点时会等概率地随机选择一个还没去过的相邻景点。
Input
第一行是两个整数n和m,分别表示景点数和道路数。 接下来行,每行三个整数Xi, Yi, Wi,分别表示第i条路径的两个景点为Xi, Yi,路径长Wi。所有景点的编号从1至n,两个景点之间至多只有一条道路。
Output
共一行,包含一个实数,即路径的期望长度,保留五位小数
Sample Input
4 3
1 2 3
2 3 1
3 4 4
Sample Output
6.00000
【样例解释】样例数据中共有6条不同的路径: 路径 长度 概率
1-->4 8 1/4
2-->1 3 1/8
2-->4 5 1/8
3-->1 4 1/8
3-->4 4 1/8
4-->1 8 1/4
因此期望长度 = 8/4 + 3/8 + 5/8 + 4/8 + 4/8 + 8/4 = 6.00
【评分方法】本题没有部分分,你程序的输出只有和标准答案的差距不超过0.01时,才能获得该测试点的满分,否则不得分。
【数据规模和约定】对于100%的数据,1 <= Wi <= 100。 测试点编号 n m 备注
1 n=10 m = n-1 保证图是链状
2 n=100 只有节点1的度数大于2
3 n=1000 /
4 n=100000 /
5 n=100000 /
6 n=10 m = n /
7 n=100 环中节点个数<=5
8 n=1000 环中节点个数<=10
9 n=100000 环中节点个数<=15
10 n=100000 环中节点个数<=20
对于一颗树的情况,还是比较简单的。
令
f[i]
表示从
i
向下的期望长度,
g[i]=len[i−>fa[i]]+∑f[fa[i]]∗siz[fa[i]]−f[i]−len[i−>fa[i]]+g[fa[i]]siz[i]
从式子中可以看出,
f[i]
要从下往上求,
g[i]
要从下往上求。
有了一个环之后,就可以把环先看成一个点,让这个点做根,求出所有点的
f
数组。因为环被当做了根,所以要先处理出环上的
环上的
g
<script type="math/tex" id="MathJax-Element-2096">g</script>的处理方法就是可以看成从环上的一个点,有概率的走到环上的下一个点,可能会增加的长度是多少。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100010;
bool flag[N];
struct S{int st,en,va;}aa[N<<1];
int n,m,tot,point[N],next[N<<1],siz[N],c[N],mark,mp[N],fa[N],pre[N],sub[N];
double f[N],g[N],a[30][30];
inline int in(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y,int z){
next[++tot]=point[x];point[x]=tot;
aa[tot].st=x;aa[tot].en=y;aa[tot].va=z;
next[++tot]=point[y];point[y]=tot;
aa[tot].st=y;aa[tot].en=x;aa[tot].va=z;
}
inline void dp_f(int x,int last){
int i;
for(i=point[x];i;i=next[i])
if(aa[i].en!=last&&flag[aa[i].en]){
dp_f(aa[i].en,x);
f[x]+=f[aa[i].en]+(double)aa[i].va;
++siz[x];fa[aa[i].en]=1;
}
if(siz[x]) f[x]/=(double)siz[x];
}
inline void dp_g(int x,int last,double value){
int i;
if(flag[x]&&siz[last]+fa[last]>1)
g[x]+=(f[last]*(double)siz[last]-f[x]-value+g[last]*fa[last])/(double)(siz[last]+fa[last]-1);
for(i=point[x];i;i=next[i])
if(aa[i].en!=last&&flag[aa[i].en]){
g[aa[i].en]=(double)aa[i].va;
dp_g(aa[i].en,x,(double)aa[i].va);
}
}
inline void find_c(int x,int last){
int i;
for(flag[x]=false,i=point[x];i;i=next[i])
if(aa[i].en!=last){
if(!flag[aa[i].en]){
mark=aa[i].en;
return ;
}
find_c(aa[i].en,x);
if(mark>0){
if(mark==x) mark=-1;
return ;
}
if(mark==-1) break;
}
flag[x]=true;
}
inline void get_c(int x,int last){
int i,o=1;
if(mp[x]) return ;
c[++tot]=x;mp[x]=tot;fa[x]=2;
for(i=point[x];i&&o;i=next[i])
if(aa[i].en!=last&&!flag[aa[i].en]){
pre[aa[i].en]=x;sub[x]=aa[i].en;
get_c(aa[i].en,x);
a[mp[x]][mp[aa[i].en]]=a[mp[aa[i].en]][mp[x]]=(double)aa[i].va;
o=0;
}
}
inline void dp_c(){
int i,u,j;
double mul;
for(i=1;i<=tot;++i){
u=c[i];
for(mul=1,j=sub[u];j!=u;j=sub[j]){
if(sub[j]!=u) g[u]+=mul*(a[mp[pre[j]]][mp[j]]+f[j]*siz[j]/(double)(siz[j]+1));
else g[u]+=mul*(a[mp[pre[j]]][mp[j]]+f[j]);
mul/=(siz[j]+1);
}
for(mul=1,j=pre[u];j!=u;j=pre[j]){
if(pre[j]!=u) g[u]+=mul*(a[mp[sub[j]]][mp[j]]+f[j]*siz[j]/(double)(siz[j]+1));
else g[u]+=mul*(a[mp[sub[j]]][mp[j]]+f[j]);
mul/=(siz[j]+1);
}
g[u]/=2.;
}
}
int main(){
int i,x,y,z;
n=in();m=in();
for(i=1;i<=m;++i){
x=in();y=in();z=in();
add(x,y,z);
}
memset(flag,1,sizeof(flag));
if(m==n-1){
dp_f(1,0);
flag[1]=false;dp_g(1,0,0);
}
else{
find_c(1,0);
for(tot=0,i=1;i<=n;++i)
if(!flag[i]){
get_c(i,0);
break;
}
for(i=1;i<=tot;++i) dp_f(c[i],0);
for(dp_c(),i=1;i<=tot;++i) dp_g(c[i],0,0);
}
double ans=0;
for(i=1;i<=n;++i)
ans+=(f[i]*siz[i]+g[i]*fa[i])/(double)(siz[i]+fa[i]);
printf("%.5f\n",ans/(double)n);
}