首先想到怎么求出每一条边 i i i 在每次游走中被经过次数的期望 f i f_i fi,那么答案就可以贪心地取。(即让最大的 f i f_i fi 权值设为 1 1 1,次大的设为 2 2 2,……,最小的设为 m m m)
但是发现不好统计,或者时间无法承受。
发现点数很小( n ≤ 500 nleq 500 n≤500),于是想到怎么通过点的期望值转移到边上。
容易得到:如果设每一个点 u u u 在每次游走中被经过次数的期望 g u g_u gu,每个点的度数为 d e g u deg_u degu,然后假设某一条边 i i i 为 ( u , v ) (u,v) (u,v),那么有 f i = g u d e g u + g v d e g v f_i=dfrac{g_u}{deg_u}+dfrac{g_v}{deg_v} fi=degugu+degvgv。
所以我们只需要求出 g g g 就可以得到 f f f 了。
容易得到状态转移方程:
g u = { 1 + ∑ ( u , v ) g v d e g v u = 1 ∑ ( u , v ) g v d e g v 1 < u < n g_u= egin{cases} 1+sumlimits_{(u,v)}dfrac{g_v}{deg_v}&u=1\ sumlimits_{(u,v)}dfrac{g_v}{deg_v}&1<u<n\ end{cases} gu=⎩⎪⎨⎪⎧1+(u,v)∑degvgv(u,v)∑degvgvu=11<u<n
解释一下:
首先为什么 u = 1 u=1 u=1 的时候要比其他时候多加 1 1 1,因为初始化的时候本来就应该是 g 1 = 1 g_1=1 g1=1。
然后为什么 g n g_n gn 不考虑,因为到了 n n n 之后就结束游走了,不可能再走向下一条边。
然后这个状态转移方程就可以看成是有 n − 1 n-1 n−1 个未知数, n − 1 n-1 n−1 条式子的方程,用高斯消元做就好了。
最后代码如下:
#include<bits/stdc++.h>
#define N 510
#define M 125010
using namespace std;
int n,m;
int deg[N],from[M],to[M];
double a[N][N],x[N],f[M];
vector<int>e[N];
void Gauss()
{
for(int i=1;i<n;i++)
{
int p=i;
for(int j=i+1;j<n;j++)
if(fabs(a[j][i])>fabs(a[p][i])) p=j;
if(i!=p) swap(a[i],a[p]);
for(int j=i+1;j<n;j++)
{
double tmp=a[j][i]/a[i][i];
for(int k=i;k<=n;k++) a[j][k]-=a[i][k]*tmp;
}
}
for(int i=n-1;i>=1;i--)
{
for(int j=i+1;j<n;j++) a[i][n]-=x[j]*a[i][j];
x[i]=a[i][n]/a[i][i];
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&from[i],&to[i]);
e[from[i]].push_back(to[i]);
e[to[i]].push_back(from[i]);
deg[from[i]]++,deg[to[i]]++;
}
for(int u=1;u<n;u++)
{
a[u][u]=1;
for(int i=0,size=e[u].size();i<size;i++)
{
int v=e[u][i];
if(v!=n) a[u][v]-=1.0/deg[v];
}
}
a[1][n]=1;
Gauss();
for(int i=1;i<=m;i++)
{
int u=from[i],v=to[i];
if(u!=n) f[i]+=x[u]/deg[u];
if(v!=n) f[i]+=x[v]/deg[v];
}
sort(f+1,f+m+1);
double ans=0;
for(int i=1;i<=m;i++)
ans+=i*f[m-i+1];
printf("%.3lf
",ans);
return 0;
}