题目大意:有一张$n$个点$m$条边的有向图,第$i$条边有个权值$d_i$表示至少要走$d_i$条边才可以经过这条边(可重复),问从点$1$到点$n$最少经过几条边(重复算多次),无解输出$-1$。$n,mleqslant150,d_ileqslant 10^9$
题解:按$d_i$排序,一条条加入转移矩阵,可以用邻接矩阵算出走恰好$k$次可以到达的位置。可以发现,在一个行的转移矩阵下,若点$1$可以走到点$n$,最多走$n-1$步。于是可以先暴力走$n-1$次,若还不能到达$n$,就用矩阵快速幂计算。复杂度$O(n^4m(log_2 d+n))$。可以令一个向量表示点$1$可以到达的点,就变成向量乘矩阵,复杂度少一个$n$,已经可以通过了。也可以用$mathrm{bitset}$优化乘法,复杂度$O(dfrac{n^3m(log_2d+n)}{omega})$
卡点:无
C++ Code:
#include <cstdio> #include <iostream> #include <algorithm> #include <bitset> const int maxn = 160; typedef std::bitset<maxn> B8; int n, m; struct Matrix { B8 s[maxn]; Matrix() { for (int i = 0; i < n; ++i) s[i].reset(); } Matrix operator * (const Matrix &rhs) { Matrix res; for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) if (s[i][j]) res.s[i] |= rhs.s[j]; return res; } } base; B8 operator * (const B8 &lhs, const Matrix &rhs) { B8 res; res.reset(); for (int i = 0; i < n; ++i) if (lhs[i]) res |= rhs.s[i]; return res; } B8 R; struct Edge { int a, b, d; inline bool operator < (const Edge &rhs) const { return d < rhs.d; } } e[maxn]; int check(int N) { for (int i = 1; i <= N; ++i) { R = R * base; if (R[n - 1]) return i; } return N + 1; } void pw(int p) { Matrix b = base; for (; p; p >>= 1, b = b * b) if (p & 1) R = R * b; } int solve() { int p = 1, res = 0; for (; p <= m && e[p].d == 0; ++p) base.s[e[p].a - 1].set(e[p].b - 1); if (p == 1) return 0; R.set(0); for (int t, o; p <= m; ++p) { t = e[p].d - e[p - 1].d; if (t <= n) { o = check(t); if (o > t) res += t; else return res + o; } else { o = check(n); if (o > n) pw(t - n), res += t; else return res + o; } base.s[e[p].a - 1].set(e[p].b - 1); } int o = check(n); if (o > n) return 0; return res + o; } int main() { std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0); std::cin >> n >> m; for (int i = 1; i <= m; ++i) std::cin >> e[i].a >> e[i].b >> e[i].d; std::sort(e + 1, e + m + 1); int ans = solve(); if (!ans) std::cout << "Impossible "; else std::cout << ans << ' '; return 0; }