1. 程式人生 > >「專題訓練」遊走(BZOJ-3143)

「專題訓練」遊走(BZOJ-3143)

題目 ans pla 公式 lar i++ vector 直接 include

題意與分析

定義走到每條邊的期望為\(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)