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;
}
posted @ 2022-10-03 22:15  Kobe303  阅读(15)  评论(0编辑  收藏  举报