「HNOI2013」游走
问题分析
发现边经过的次数实际上就是点经过的次数乘上概率。那么问题就变成了求每个点经过的次数。
把无向边拆成两条有向边,然后把点 \(n\) 的所有出边都删掉。然后高斯消元即可。每个点经过的次数就是可以走到它的点的次数乘上概率之和。当然点 \(1\) 要额外加 \(1\) ,因为一开始是在点 \(1\) 。
参考程序
#include <cstdio>
#include <algorithm>
#include <cmath>
#define Maxn 510
#define Eps 1e-12
int n, m, Map[Maxn][Maxn], Sz[Maxn], Id[Maxn * Maxn];
double A[Maxn][Maxn], Times[Maxn * Maxn];
inline int Cmp(double x, double y) {
if (fabs(x - y) < Eps) return 0;
if (x - y > Eps) return -1; else return 1;
}
inline void Gauss() {
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; ++j)
if (Cmp(A[i][j], 0) != 0) {
for (int k = 1; k <= n + 1; ++k)
std::swap(A[i][k], A[j][k]);
break;
}
double Tmp = A[i][i];
for (int j = 1; j <= n + 1; ++j) A[i][j] /= Tmp;
for (int j = 1; j <= n; ++j) {
if (i == j) continue;
Tmp = A[j][i];
for (int k = 1; k <= n + 1; ++k)
A[j][k] -= Tmp * A[i][k];
}
}
return;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1, x, y; i <= m; ++i) {
scanf("%d%d", &x, &y);
++Sz[x]; Map[x][y] = i;
++Sz[y]; Map[y][x] = i;
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j < n; ++j)
if (Map[j][i])
A[i][j] = -1.0 / Sz[j];
for (int i = 1; i <= n; ++i) A[i][i] = 1;
A[1][n + 1] = 1;
Gauss();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
Times[Map[i][j]] = ((i != n) ? A[i][n + 1] / Sz[i] : 0) + ((j != n) ? A[j][n + 1] / Sz[j] : 0);
for (int i = 1; i <= m; ++i) Id[i] = i;
std::sort(Id + 1, Id + m + 1, [](int x, int y)->bool{ return Cmp(Times[x], Times[y]) == -1;});
double Ans = 0;
for (int i = 1; i <= m; ++i) Ans += Times[Id[i]] * i;
printf("%.3lf\n", Ans);
return 0;
}