码迷,mamicode.com
首页 > 其他好文 > 详细

【CodeForces576D】Flights for Regular Customers

时间:2020-07-16 00:11:18      阅读:64      评论:0      收藏:0      [点我收藏+]

标签:amp   调用   ble   str   时间复杂度   时间   矩阵快速幂   bre   去掉   

题目链接

点击打开链接

题目解法

考虑枚举最优状态是哪些边解锁了,哪些边没有。这样真的有 \(2^n\) 种情况吗?并不是的。将所有边按照解锁需要走过边的数量排序。解锁边的顺序必然是排好序的这样。所以考虑只解锁前 \(i\) 条边,走到 \(n\) 的最短距离是多少。

所以我们可以这样:每次枚举新解锁了哪一条边,如果当前边需要走 \(i\) 步才能解锁,那么看走 \(i\) 条边并解锁这个边后可以到哪些点,用那些点跑一边多源 dfs 寻找到终点的最短路,更新答案即可。

然而这时间复杂度 \(\mathcal O(n^3m\log n)\) 并不能过去。瓶颈是矩阵快速幂。由于矩阵是0/1 矩阵,所以使用bitset优化即可。比较无脑的bitset可以维护矩阵的行和列,维护两个bitset数组,这样比较方便写。当然也可以只维护一个bitset,这样难理解,而且非常非常没必要。

总结

  • bitset 优化0/1矩阵的乘法。
  • 解锁顺序可以去掉一些边的解锁状态。

代码

#include <cstdio>
#include <bitset>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const int NM = 155;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
typedef bitset<150> bt;
int n, m;
struct Edge {
	int u, v, w;
	inline bool operator < (const Edge &d) const { return w < d.w; }
} eg[NM];
struct Mat {
	bt A[150];
	inline bt & operator [] (const int i) { return A[i]; }
	inline const bt & operator [] (const int i) const { return A[i]; }
	//因为 Mat * 不能保存 const Mat * 的东西
	//所以必须有一种返回 const Mat * 的调用方法 
} A;
bt operator * (const Mat &A, const bt &B) {
	static bt ret;
	for (int i = 0; i < n; ++i) ret[i] = (A[i] & B).any();
	return ret;
}
Mat operator * (const Mat &A, const Mat &B) {
	static Mat ret;
	for (int i = 0; i < n; ++i) ret[i].reset();
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < n; ++j)
			if (A[i][j]) ret[i] |= B[j];
	return ret;
}
void ksm(bt &A, Mat B, int c) {
	for (; c; c >>= 1, B = B * B)
		if (c & 1) A = B * A;
}
bt go;
queue<int> Q;
ll dis[NM];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; ++i) {
		scanf("%d%d%d", &eg[i].u, &eg[i].v, &eg[i].w);
		--eg[i].u, --eg[i].v;
	}
	sort(eg + 1, eg + m + 1);
	go[0] = 1;
	ll ans = infll;
	for (int i = 1, t = 0; i <= m; ++i) {
		if (eg[i].w >= ans) break ;
		if (eg[i].w != t) ksm(go, A, eg[i].w - t);
		t = eg[i].w;
		A[eg[i].v][eg[i].u] = 1;
		for (int j = 0; j < n; ++j)
			if (go[j]) Q.push(j), dis[j] = 0;
			else dis[j] = infll;
		while (Q.size()) {
			int u = Q.front();
			Q.pop();
			for (int j = 0; j < n; ++j)
				if (A[j][u] && dis[j] == infll) {
					dis[j] = dis[u] + 1;
					Q.push(j);
				}
		}
		ans = min(ans, t + dis[n - 1]);
	}
	if (ans == infll) printf("Impossible\n");
	else printf("%lld\n", ans);
	return 0;
}

【CodeForces576D】Flights for Regular Customers

标签:amp   调用   ble   str   时间复杂度   时间   矩阵快速幂   bre   去掉   

原文地址:https://www.cnblogs.com/YouthRhythms/p/13307435.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!