ATM 寻宝题解

挂飞了呜呜呜……100 挂成 10 了。。。

讲一下我的解法吧。

对于 k=1,明显只要一直用魔法就行了。直接可以看成边权为 1 的无向图,跑 bfs 就行了。

对于其他情况,我们可以先预处理出用魔法的边权。可以把边权存在一个数组里,我用 p_i 表示:已经使用过 i 次魔法时的边权。因为这时 k \ge 2,数组开 \log_2 10^9 \approx 30 就行。

注意预处理边权时 k 可能较大,开 long long。图中原本边权 w_i \le 10^9,所以一旦 p_i > 10^9 就停止处理。

然后直接跑最短路就行了。但是蒟蒻爆炸了,赛时 Runtime Error 10

#include <bits/stdc++.h>
#define fi first
#define se second

using namespace std;

using PII = pair<int, int>;
using i32 = int32_t;
using i64 = int64_t;
using ui32 = uint32_t;
using ui64 = uint64_t;

int _;

namespace swate {
	inline int read() {int s = 0, w = 1;char ch = getchar();while (ch < '0' || ch > '9') {if (ch == '-') w *= -1;ch = getchar();}while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + (ch ^ 48);ch = getchar();}return s * w;}
	inline i64 read2() {i64 s = 0, w = 1;char ch = getchar();while (ch < '0' || ch > '9') {if (ch == '-') w *= -1;ch = getchar();}while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + (ch ^ 48);ch = getchar();}return s * w;}
	inline void write(i64 x) {if (x < 0) {putchar('-');x = -x;}if (x > 9) write(x / 10); putchar(x % 10 + '0');}
	
	const int N = 1e5 + 5, M = 2e5 + 5;
	int n, m, k, s, t;
	vector<pair<int, i64> > G[N];
	i64 dis[N][35];
	bool vis[N][35];
	i64 p[35], f = 30;
	
	inline void init() {
		
	}
	
	queue<PII> qu;	
	
	inline void bfs1() {
		dis[s][0] = 0;
		qu.push({s, 0});
		while (!qu.empty()) {
			PII x = qu.front();
			int u = x.fi, d = x.se;
			qu.pop();
			
			if (vis[u][d]) continue;
			vis[u][d] = 1;
			
			for (auto it : G[u]) {
				int v = it.fi;
				i64 w = it.se;
				if (d < f && p[d] > 0 && dis[v][d + 1] > dis[u][d] + p[d]) {
					dis[v][d + 1] = dis[u][d] + p[d];
					qu.push({v, d + 1});
				}
				if (dis[v][d] > dis[u][d] + w) {
					dis[v][d] = dis[u][d] + w;
					qu.push({v, d});
				}
			}
		}
	}
	
	inline void bfs2() {
		qu.push({s, 0});
		while (!qu.empty()) {
			PII x = qu.front();
			int u = x.fi, d = x.se;
			qu.pop();
			
			if (u == t) {
				cout << d;
				return ;
			}
			
			for (auto it : G[u]) {
				int v = it.fi;
				qu.push({v, d + 1});
			}
		}
	}
	
	inline void solve() {
		n = read(), m = read(), k = read();
		for (int i = 1, u, v; i <= m; ++i) {
			i64 w;
			u = read(), v = read(), w = read2();
			G[u].push_back({v, w});
			G[v].push_back({u, w});
		}
		s = read(), t = read();
		
		if (k == 1) {
			bfs2();
			return ;
		} else {
			p[0] = 1;
			for (int i = 1; i <= 30; ++i) {
				p[i] = p[i - 1] * k;
				if (p[i] < 0 || p[i] > 1000000000) {
					f = i;
					break;
				}
			}	
			memset(dis, 0x3f, sizeof dis);
			bfs1();
			i64 ans = 0x3f3f3f3f3f3f3f3f;
			for (int i = 1; i <= f; ++i) {
				ans = min(ans, dis[t][i]);
			}
			write(ans);
		}
	}
}

//#define Test

i32 main() {
	freopen("sum.in", "r", stdin);
	freopen("sum.out", "w", stdout);
#ifdef Test 
	cin >> _;
#else
	_ = 1;
#endif

	for (int i = 1; i <= _; ++i) {
		swate::init();
		swate::solve();
	}
	
	return 0;
}

这里总结一下死因:

  • subtask 4k=1,炸了因为没有判环,在跑 bfs 时应该打 vis 标记。
  • subtask 2 wa 了,原因很简单,我写的是普通队列,一个点可以可以有多条边通向,所以只要把下面的删掉就可以 AC。
if (vis[u][d]) continue;
vis[u][d] = 1;

但是这样不会死循环吗?不会,因为边至多 30 条,不会死循环。这样甚至比用优先队列的跑得快。

1 个赞

有没有神犇告诉我这题怎么还可以分层图啊

诶不对我的思路和分层图差不多

ytx!哥,你英语精彩练习A本带了吗?英语笔记忘记写精彩练习了!

私一下,有点事

%%%tql