题目链接
首先,我们想要求总的期望,那么我们发现如果我们能求出每条边的期望,之后对所有边的期望排序,贪心地给期望经过次数多的边赋小的编号,给期望经过次数少的边赋大的编号即可。
那么我们需要考虑如何求所有边的期望经过次数。
我们发现,每条边的期望次数是
,其中
和
是当前边的两个端点,
数组表示点
的期望经过次数,
表示在无向图中点
的度数。
我们发现
数组我们可以在读入这些边的时候顺便处理出来,那么我们只需要求出每个点期望经过的次数即可了。
那么我们考虑如何求每个点的期望经过次数。我们发现,每个点的期望经过次数是
。特别地,根据题意,
号点经过并且只经过一次,所以
。另外,
号点为起点,一开始就会经过一次,所以
号点的期望步数是
。
我们发现,我们可以对每个点列一个方程,这样可以高斯消元解出每一个点的期望经过次数。有了每个点的期望经过次数之后,我们可以通过之前的讲解求出每条边的期望步数并贪心地给边编号,最后统计答案。
思路就是这样子,实现的时候我觉得在高斯消元时要注意一下细节,特别是对于
号点,它的
值是
,但是我们在实际操作时把第
行只有
的系数设为
,其他的所有系数都设为
,包括增广的那一列也是0。但是这样我们会发现最后一个式子成了
。这样做是因为我们在考虑每个点给每条边的影响时,
号点是不会对与它相连的边产生贡献的,因为到了
号点就不能再返回了,所以为了正确地统计边的期望经过次数,强制将点
的期望经过次数设为了
,实际上应该是
的。而把
的系数设为
的原因是为了避免在做
个元的高斯消元时
时因为除以
而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;
}