CF576D
首先对边排序,然后从小到大依次加入每条边。
考虑只有当前存在的边时,答案怎么求。
假设此时加入的边的 \(d\) 值为 \(t\),首先要求的是经过恰好 \(t\) 条边时可以到达哪些点。
这个可以从加入上一条边时的答案递推过来,这个递推式可以矩阵加速。
求出恰好 \(t\) 条边时可以到达哪些点后,对整个图进行一次多源 bfs 即可求出当前的答案。
这样时间复杂度是 \(\mathcal O(n^3m\log d)\) 的,无法通过。
注意到每个点的状态只有 \(0/1\) 两种,分别对应着无法到达和可以到达。
因此可以 bitset 优化,时间复杂度 \(\mathcal O(\dfrac{n^3m\log d}{w})\)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 155;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, m;
ll ans = inf, t, dif;
ll d[N];
queue <int> q;
struct node {
int u, v; ll w;
void get() { scanf("%d%d%lld", &u, &v, &w); }
bool operator < (const node &x) const {
return w < x.w;
}
} E[N];
struct mat {
bitset <N> a[N];
mat () {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
a[i][j] = 0;
}
void set() {
for (int i = 1; i <= n; ++i)
a[i][i] = 1;
}
mat operator * (const mat &x) {
mat res;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (a[i][j])
res.a[i] |= x.a[j];
return res;
}
} f, g;
mat qpow(mat x, ll y) {
mat res; res.set();
while (y) {
if (y & 1) res = res * x;
x = x * x;
y >>= 1;
}
return res;
}
void bfs() {
memset(d, 0x3f, sizeof d);
for (int i = 1; i <= n; ++i)
if (f.a[1][i]) q.push(i), d[i] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v = 1; v <= n; ++v)
if (g.a[u][v] && d[v] == inf)
d[v] = d[u] + 1, q.push(v);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) E[i].get();
sort(E + 1, E + m + 1);
f.a[1][1] = 1, d[n] = inf;
for (int i = 1; i <= m; ++i) {
if (ans < E[i].w) break;
dif = E[i].w - t, t = E[i].w;
f = f * qpow(g, dif);
g.a[E[i].u][E[i].v] = 1;
if (i == m || E[i].w != E[i + 1].w) bfs();
ans = min(ans, t + d[n]);
}
if (ans == inf) printf("Impossible");
else printf("%lld", ans);
return 0;
}