题目链接

CodeForces576D

题目概括

给定 $n$ 个点 $m$ 条边的有向图,每条边三元组的形式给定 $(u,v,k)$ 表示连接了一条有向边 $u\rightarrow v$ ,要通过这条边必须要走过 $k$ 条边。

问最少需要通过多少条边从 $1$ 号节点到 $n$ 号节点。

数据范围 $n,m\leq 150,k\leq 10^9$

思路要点

首先需要知道一个知识

对于一个邻接矩阵 $A$,$A^k$ 中 $(i,j)$ 元素就是从 $i$ 到 $j$,恰好走过了 $k$ 步的方案数。

此题首先按照边权排序,考虑从小到大加入边。

我们会发现,现在能够到达的点集只和上一条加入的边后得到的到达的点集有关。

这样我们就用上面的知识维护可以到达的点集,这个地方可以用 $bitset$ 维护矩阵转移(注意 $bitset$ 的转置)

对于每一个可以到达的点集,我们考虑从当前点到终点的答案,用最短路算法计算出到终点的距离,然后用点集内的点更新答案。

时间复杂度 $\mathcal O(\frac {mn^3log_2 k}w)$

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 150;
const int M = 150;

struct Edge {
  int u, v, w;
  
  bool operator < (Edge b) const {
    return w < b.w;
  }
} edge[M];

int n, m;
int dis[N][N];

struct Matrix {
  bitset<150> a[150];
  
  Matrix() {
    for (int i = 0; i < n; i++) {
      a[i].reset();
    }
  }
  
  Matrix(int x) {
    for (int i = 0; i < n; i++) {
      a[i].reset();
    }
    for (int i = 0; i < n; i++) {
      a[i][i] = x;
    }
  }
  
  void reverse() {
    Matrix t;
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        t.a[i][j] = a[j][i];
      }
    }
    *this = t;
  }
  
  bitset<150>& operator [](int i) {
    return a[i];
  } 
};

ostream& operator << (ostream& out, Matrix a) {
  out << "[" << '\n';
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      out << a[i][j] << ' ';
    }
    out << '\n';
  }
  out << "]" << '\n';
  return out;
}

Matrix now;

Matrix operator * (Matrix x, Matrix y) {
  Matrix res(0);
  y.reverse();
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      res.a[i][j] = (x[i] & y[j]).any();
    }
  }
  return res;
}

Matrix operator ^ (Matrix x, long long y) {
  Matrix res(1);
  for (; y; y >>= 1, x = x * x) {
    if (y & 1) {
      res = res * x;
    }
  }
  return res;
}

template <class T> 
void chkmax(T& x, T y) {
  x = x > y ? x : y;
}

template <class T> 
void chkmin(T& x, T y) {
  x = x < y ? x : y;
}

void test() {
  cerr << "begin test" << '\n';
  Matrix a, b, c;
  a[0][0] = 1, a[0][1] = 1, a[0][2] = 1;
  a[1][0] = 0, a[1][1] = 0, a[1][2] = 1;
  a[2][0] = 0, a[2][1] = 0, a[2][2] = 0;
  
  b[0][0] = 0, b[0][1] = 1, b[0][2] = 0;
  b[1][0] = 0, b[1][1] = 1, b[1][2] = 0;
  b[2][0] = 0, b[2][1] = 1, b[2][2] = 0;
  c = a * b;
  cerr << a << b << c << '\n';
  c = b * a;
  cerr << a << b << c << '\n';
  cerr << "end test" << '\n';
}

int main() {
  ios::sync_with_stdio(false);
  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    cin >> edge[i].u >> edge[i].v >> edge[i].w;
    edge[i].u--;
    edge[i].v--;
  }
  sort(edge, edge + m);
//  for (int i = 0; i < m; i++) {
//    cerr << edge[i].u << ' ' << edge[i].v << ' ' << edge[i].w << '\n';
//  }
  int ans = 0x3f3f3f3f, lst = 0;
  Matrix sta(0), road(0);
  sta[0][0] = 1;
  for (int i = 0; i < m; i++) {
    int u = edge[i].u, v = edge[i].v, w = edge[i].w;
    sta = sta * (road ^ (w - lst));
    road[u][v] = 1;
    for (int j = 0; j < n; j++) {
      for (int k = 0; k < n; k++) {
        if (road[j][k]) {
          dis[j][k] = 1;
        } else {
          dis[j][k] = 0x3f3f3f3f;
        }
//        cout << dis[i][j] << ' ';
      }
//      cout << '\n';
    }
//    cout << '\n';
    for (int mid = 0; mid < n; mid++) {
      for (int j = 0; j < n; j++) {
        for (int k = 0; k < n; k++) {
          if (j != mid && mid != k && j != k) {
            chkmin(dis[j][k], dis[j][mid] + dis[mid][k]);
          }
        }
      }
    }
    for (int j = 0; j < n - 1; j++) {
      if (sta[0][j]) {
//        cerr << "debug1 : " << j << '\n';
        chkmin(ans, w + dis[j][n - 1]);
      }
    }
    lst = w;
  }
  if (ans == 0x3f3f3f3f) {
    cout << "Impossible" << '\n';
  } else {
    cout << ans << '\n';
  }
  return 0;
}
最后修改:2020 年 03 月 28 日 10 : 32 PM
如果觉得我的文章对你有用,请随意赞赏