「專題訓練」遊走(BZOJ-3143)
阿新 • • 發佈:2019-04-19
題目 ans pla 公式 lar i++ vector 直接 include 。於是問題又轉化成走到各個點的期望。
如果這是個DAG,那麽問題是一個dp(\(dp[j]\)表示從j到n的期望長度,\(dp[i] = \sum_{i\to j} \frac{dp[j]+1}{degree_i}\),具體問題見BZOJ-3036)。問題是這題並不是,場面有點尷尬。但是上面DAG的方程加起來共有n個,而我們要求n-1個解,這提示了我們用高斯消元解決這個問題。用高斯消元的話我們就把公式變形一下:
\[e_i = \sum_{j=1}^n p_{i,j}e_{j} , 2\le i \le n\]
其中\(p_{i,j}\)表示從j走到i的概率,對於第一個點該式子的右邊還要加上一個1(因為一開始就在這個點上了,不妨思考下平凡的情況)。這樣一來就可以用高斯消元解決這一題了。
題意與分析
定義走到每條邊的期望為\(e_i\),一開始的想法是給定一個\(\large\sum_{i=1}^n e_i a_i\),求一個a的排列使得這個和最小。問題在於這樣等於沒對題目作分析,而且題目的難度沒有被轉化降低。於是(在高人指點下)我們想到,如果先求出\(e_i\),然後按照從小到大的順序貪心的編號,問題就直接轉化成求走到每個邊的期望。
邊的期望是一個新操作,但是其實不難:定義走到點的期望是\(f_i\),考慮一條邊\((u, v)\),對於這條邊而言,只有從u和從v才能走到這條邊,那麽它的期望就是\(\large e_i=\frac{f_u}{degree_u}+\frac{f_v}{degree_v}\)
如果這是個DAG,那麽問題是一個dp(\(dp[j]\)表示從j到n的期望長度,\(dp[i] = \sum_{i\to j} \frac{dp[j]+1}{degree_i}\),具體問題見BZOJ-3036)。問題是這題並不是,場面有點尷尬。但是上面DAG的方程加起來共有n個,而我們要求n-1個解,這提示了我們用高斯消元解決這個問題。用高斯消元的話我們就把公式變形一下:
\[e_i = \sum_{j=1}^n p_{i,j}e_{j} , 2\le i \le n\]
其中\(p_{i,j}\)表示從j走到i的概率,對於第一個點該式子的右邊還要加上一個1(因為一開始就在這個點上了,不妨思考下平凡的情況)。這樣一來就可以用高斯消元解決這一題了。
問題的逐步轉化是這一題的關鍵。
代碼
#include <bits/stdc++.h> using namespace std; vector<pair<int, int>> edges; vector<int> G[505]; double arr[505][505]; int main() { int n, m; while (cin >> n >> m) { for (int i = 1, u, v; i <= m; ++i) { cin >> u >> v; if (u > v) swap(v, u); edges.push_back(make_pair(u, v)); G[u].push_back(v); G[v].push_back(u); } memset(arr, 0, sizeof(arr)); for (int i = 1; i <= n; ++i) arr[i][i] = -1.0; arr[1][n + 1] = -1.0; for (int i = 1; i < n; i++) { for (int j = 0; j < G[i].size(); ++j) arr[G[i][j]][i] += 1.0 / (int)G[i].size(); } for (int i = 1; i <= n; ++i) { int idx = i; for (int j = i + 1; j <= n; ++j) if (fabs(arr[j][i]) > fabs(arr[idx][i])) idx = j; assert(fabs(arr[idx][i]) > 1e-10); if (idx != i) for (int j = i; j <= n + 1; ++j) swap(arr[i][j], arr[idx][j]); for (int j = 1; j <= n; ++j) if (i != j) { double t = arr[j][i] / arr[i][i]; for (int k = i; k <= n + 1; ++k) arr[j][k] -= arr[i][k] * t; } } /* for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n + 1; ++j) { cout << arr[i][j] << " "; } cout << endl; } */ double e_node[505]; double e_edge[500 * 500 / 2 + 5]; memset(e_edge, 0, sizeof(e_edge)); memset(e_node, 0, sizeof(e_node)); for (int i = 1; i <= n; ++i) { e_node[i] = arr[i][n + 1] / arr[i][i]; } for (int i = 0; i < edges.size(); ++i) { e_edge[i] += e_node[edges[i].first] / G[edges[i].first].size(); if (edges[i].second != n) e_edge[i] += e_node[edges[i].second] / G[edges[i].second].size(); } sort(e_edge, e_edge + m); double ans = 0; for (int i = 0; i < m; ++i) ans += e_edge[i] * (m - i); cout << fixed << setprecision(3) << ans << endl; } return 0; }
「專題訓練」遊走(BZOJ-3143)