BZOJ3143 「HNOI2013」游走

Description

$n$ 个点 $m$ 条边的无向连通图,在上面从 $1$ 号点开始随机游走。现在你可以给每条边从 $1$ 到 $m$ 编号作为分数(经过就得分)。求如何编号使得总分的期望最小。输出这个最小值即可。

$n \leq 500$

Solution

如果知道了每条边被经过的期望次数,那么根据排序不等式显然是逆序分配最小。

设 $f_u$ 是 $u$ 点被经过的期望次数,$deg_u$ 表示 $u$ 的度数。那么边 $(u, v)$ 被经过的期望次数是

$f$ 的求法比较简单,即

高斯消元一波再排个序就做完了。时间复杂度 $O(n^3)$

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* Author: AcFunction
* Date: 2019-03-18 22:13:03
* Email: 3486942970@qq.com
**/

#include <bits/stdc++.h>
#define ll long long
#define db double
#define PII pair <int, int>
#define pb push_back
#define Fi first
#define Se second
#define MP make_pair
#define RG register
#define rep(i, l, r) for(RG int i = l; i <= r; i++)
#define per(i, r, l) for(RG int i = r; i >= l; i--)

using namespace std;

void INIT() {
ios :: sync_with_stdio(false); cin.tie(0);
}

const int N = 505;

int n, m, deg[N];
PII E[N * N];
db a[N][N], t[N];
db A[N * N], ans;
vector <int> g[N];

void build(int n) {
rep(u, 1, n) {
a[u][u] = 1.0;
for(auto v : g[u])
if(v != n)
a[u][v] -= 1.0 / deg[v];
} a[1][n + 1] = 1.0;
a[n][n + 1] = -1.0;
}

void gauss(int n) {
rep(i, 1, n) {
int p = i;
rep(j, i + 1, n)
if(fabs(a[j][i]) > fabs(a[p][i] + 1e-13))
p = j;
rep(j, i + 1, n) {
if(fabs(a[j][i]) < 1e-13) continue ;
db cof = a[j][i] / a[i][i];
rep(k, i, n + 1)
a[j][k] -= a[i][k] * cof;
}
}
per(i, n, 1) {
rep(j, i + 1, n)
a[i][n + 1] -= a[i][j] * t[j];
t[i] = a[i][n + 1] / a[i][i];
}
}

int main() {
INIT();
cin >> n >> m;
rep(i, 1, m) {
int u, v; cin >> u >> v;
E[i] = MP(u, v);
deg[u]++, deg[v]++;
g[u].pb(v), g[v].pb(u);
} build(n), gauss(n);
rep(i, 1, m)
A[i] = 1.0 * t[E[i].Fi] / deg[E[i].Fi] +
1.0 * t[E[i].Se] / deg[E[i].Se];
sort(A + 1, A + m + 1);
rep(i, 1, m) ans += A[i] * (m - i + 1);
printf("%.3lf\n", ans);
return 0;
}