zoukankan      html  css  js  c++  java
  • 「专题训练」游走(BZOJ-3143)

    题意与分析

    定义走到每条边的期望为(e_i),一开始的想法是给定一个(largesum_{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 o j} frac{dp[j]+1}{degree_i}),具体问题见BZOJ-3036)。问题是这题并不是,场面有点尴尬。但是上面DAG的方程加起来共有n个,而我们要求n-1个解,这提示了我们用高斯消元解决这个问题。用高斯消元的话我们就把公式变形一下:

    [e_i = sum_{j=1}^n p_{i,j}e_{j} , 2le 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;
    }
    
  • 相关阅读:
    国庆·生日
    国足
    Eason's concert
    今天的斩获
    The 4400
    闷热
    24
    一直登录不了。。。原来是因为。。。
    黄色暴雨警告
    绝密飞行
  • 原文地址:https://www.cnblogs.com/samhx/p/bzoj-3143.html
Copyright © 2011-2022 走看看