[CF576D]Flights for Regular Customers
题目大意:有一张$n$个点$m$条边的有向图,第$i$条边有个权值$d_i$表示至少要走$d_i$条边才可以经过这条边(可重复),问从点$1$到点$n$最少经过几条边(重复算多次),无解输出$-1$。$n,m\leqslant150,d_i\leqslant 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\n";
else std::cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号