题目
思路
其实是一道平凡的题目:直接求出每条边的期望经过次数,然后贪心分配编号。而边的期望经过次数,就是两个端点的 ( ( ( 期望经过次数 ÷ \div ÷ 度数 ) ) ) 相加。然后解每个点的期望经过次数就行了。
只不过我今天突然有点脑抽:凭什么 e x = ∑ ⟨ x , y ⟩ e y deg ( y ) e_x=\sum_{\langle x,y\rangle}\frac{e_y}{\deg(y)} ex=∑⟨x,y⟩deg(y)ey 呢?我一下子没反应过来。思考了一下,又恍然大悟;决定还是记录一下这个插曲吧。
有趣的事情是,其实 e x e_x ex 不仅是期望经过次数,还是 以 x x x 结尾的路径的概率之和。因为之后无论怎么走,一定走到终点,所以后面的部分的概率之和是 1 1 1 。然后乘上前面的固定部分的概率,就是这个 x x x 提供的期望经过次数。如果对每个固定部分(以 x x x 结尾的路径)求和,可以发现,每个 x x x 都被计算了恰好一次。
那么此时 e y deg ( y ) e_{y}\over\deg(y) deg(y)ey 就是所有以 y → x y\to x y→x 结尾的路径的概率之和。求和就是 e x e_x ex 了嘛。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <cmath>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MAXN = 505;
double a[MAXN][MAXN];
void gauss(int n){
for(int i=1,rid; i<=n; ++i){
rep(j,(rid=i)+1,n)
if(fabs(a[j][i]) > fabs(a[rid][i]))
rid = j; // find maximum
swap(a[rid],a[i]);
rep(j,1,n) if(j != i && a[j][i]){
for(int k=n+1; k!=i; --k)
a[j][k] -= a[i][k]*a[j][i]/a[i][i];
a[j][i] = 0; // it will be
}
}
}
int deg[MAXN];
bool g[MAXN][MAXN];
double val[MAXN*MAXN];
int main(){
int n = readint(), m = readint();
for(int x,y; m; --m){
x = readint(), y = readint();
++ deg[x], ++ deg[y];
g[x][y] = g[y][x] = true;
a[x][y] = a[y][x] = 1;
}
rep(i,1,n-1) rep(j,1,n) // <i,j>
if(a[j][i] != 0) a[j][i] /= deg[i];
rep(j,1,n) a[j][n] = 0; // no contribution
rep(i,1,n) -- a[i][i]; // standard formula
a[1][n+1] = -1; gauss(n); a[n][n+1] = 0;
rep(i,1,n) rep(j,i+1,n) if(g[i][j])
val[++ m] = a[i][n+1]/a[i][i]/deg[i]
+ a[j][n+1]/a[j][j]/deg[j];
sort(val+1,val+m+1,greater<double>());
double ans = 0;
for(int i=1; i<=m; ++i) ans += i*val[i];
printf("%.3f\n",ans);
return 0;
}