题目链接
首先,我们想要求总的期望,那么我们发现如果我们能求出每条边的期望,之后对所有边的期望排序,贪心地给期望经过次数多的边赋小的编号,给期望经过次数少的边赋大的编号即可。
那么我们需要考虑如何求所有边的期望经过次数。
我们发现,每条边的期望次数是
dpxdegx+dpydegy
d
p
x
d
e
g
x
+
d
p
y
d
e
g
y
,其中
x
x
和是当前边的两个端点,
dpi
d
p
i
数组表示点
i
i
的期望经过次数,表示在无向图中点
i
i
的度数。
我们发现数组我们可以在读入这些边的时候顺便处理出来,那么我们只需要求出每个点期望经过的次数即可了。
那么我们考虑如何求每个点的期望经过次数。我们发现,每个点的期望经过次数是
dpv=∑v到u有边u dpudegu
d
p
v
=
∑
u
v
到
u
有
边
d
p
u
d
e
g
u
。特别地,根据题意,
n
n
号点经过并且只经过一次,所以。另外,
1
1
号点为起点,一开始就会经过一次,所以号点的期望步数是
dp1=∑1到u有边u dpudegu+1
d
p
1
=
∑
u
1
到
u
有
边
d
p
u
d
e
g
u
+
1
。
我们发现,我们可以对每个点列一个方程,这样可以高斯消元解出每一个点的期望经过次数。有了每个点的期望经过次数之后,我们可以通过之前的讲解求出每条边的期望步数并贪心地给边编号,最后统计答案。
思路就是这样子,实现的时候我觉得在高斯消元时要注意一下细节,特别是对于
n
n
号点,它的值是
1
1
,但是我们在实际操作时把第行只有
dpn
d
p
n
的系数设为
1
1
,其他的所有系数都设为,包括增广的那一列也是0。但是这样我们会发现最后一个式子成了
dpn=0
d
p
n
=
0
。这样做是因为我们在考虑每个点给每条边的影响时,
n
n
号点是不会对与它相连的边产生贡献的,因为到了号点就不能再返回了,所以为了正确地统计边的期望经过次数,强制将点
n
n
的期望经过次数设为了,实际上应该是
1
1
的。而把的系数设为
1
1
的原因是为了避免在做个元的高斯消元时
dp(n+1)/dpn
d
p
(
n
+
1
)
/
d
p
n
时因为除以
0
0
<script id="MathJax-Element-32" type="math/tex">0</script>而RE。主要还是我高斯消元写得有点少。
最后是代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,hed[1000],cnt;
struct node
{
int from,to,next;
}a[5000010];
double dp[1001],b[502][502],ans,e[5000100],deg[510];
void add(int from,int to)
{
a[++cnt].to=to;
a[cnt].from=from;
a[cnt].next=hed[from];
hed[from]=cnt;
}
void gauss()
{
for(int i=1;i<=n;++i)
{
int r=i;
for(int j=i+1;j<=n;++j)
{
if(abs(b[j][i])>abs(b[r][i]))
r=j;
}
if(r!=i)
swap(b[r],b[i]);
for(int j=i+1;j<=n;++j)
{
double t=b[j][i]/b[i][i];
for(int k=i;k<=n+1;++k)
b[j][k]-=t*b[i][k];
}
}
for(int i=n;i>=1;--i)
{
for(int j=n;j>i;--j)
b[i][n+1]-=b[i][j]*b[j][n+1];
b[i][n+1]/=b[i][i];
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);//正向边和反向边只算一条
++deg[x];
++deg[y];
}
for(int i=1;i<=n-1;++i)
b[i][i]=-1;
for(int i=1;i<=m;++i)
{
b[a[i].from][a[i].to]+=(double)1.0/deg[a[i].to];
b[a[i].to][a[i].from]+=(double)1.0/deg[a[i].from];
}
for(int i=1;i<=n-1;++i)
b[n][i]=0;
b[n][n]=1;
b[1][n+1]=-1;
gauss();
for(int i=1;i<=m;++i)
e[i]=b[a[i].from][n+1]/deg[a[i].from]+b[a[i].to][n+1]/deg[a[i].to];
sort(e+1,e+m+1);
for(int i=1;i<=m;++i)
ans+=e[i]*(m-i+1);
printf("%.3lf\n",ans);
return 0;
}