题目链接
题目概括
给定\(n\)个点\(m\)条边的有向图,每条边三元组的形式给定\((u,v,k)\)表示连接了一条有向边\(u->v\),要通过这条边必须要走过\(k\)条边。
问最少需要通过多少条边从\(1\)号节点到\(n\)号节点。
数据范围 \(n,m\leq 150,k\leq 10^9\)
思路要点
首先需要知道一个知识
对于一个邻接矩阵\(A\),\(A^k\)中\((i,j)\)元素就是从\(i\)到\(j\),恰好走过了\(k\)步的方案数。
此题首先按照边权排序,考虑从小到大加入边。
我们会发现,现在能够到大的点集之和上一条加入的边后得到的到达的点集有关。
这样我们就用上面的知识维护可以到达的点集,这个地方可以用\(bitset\)维护矩阵转移(注意\(bitset\)的转置)
对于每一个可以到达的点集,我们考虑从当前点到终点的答案,用最短路算法计算出到终点的距离,然后用点集内的点更新答案。
时间复杂度 \(\mathcal O(mn^2log_2 kw)\)
其中w为bitset的复杂度
代码
#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;
}