题解:P7669 [JOI2018] Commuter Pass

Solution

我们有结论:答案路径上免费的边是连续的。

证明比较显然,如果不连续就可以得出 \(S\to T\)\(U\to V\) 的更优路径。

比较暴力的做法就是枚举免费路径的起点和终点(我们记为 \(x,y\)),若 \(dis_{S,x}+dis_{y,T}+dis_{x,y}=dis_{S,T}\),用 \(dis_{U,x}+dis_{y,V}\) 更新答案。

我们要判断 \((x,y)\) 这条路径是否在最短路上,自然想到建最短路 DAG,\((x,y)\) 在最短路上的充要条件就是 \((x,y)\) 在最短路 DAG 上存在。

DAG 的可达性是很好的偏序关系。我们枚举 \(x\),令 \(mn_x\) 是所有可达的 \(y\) 中最小的 \(dis_{y,V}\),拓扑排序转移即可。答案就是 \(\min\{dis_{U,x}+mn_x\}\)

当然,路径正反不定,还要令 \(mn=\min dis_{U,y}\) 跑一遍,再和 \(\min\{dis_{x,V}+mn_x\}\) 取最小值。

时间复杂度 \(\mathcal{O}(\text{shortestpath}(n,m))\)(就是你选的最短路算法的复杂度,比如我写个 Dijkstra 就是 \(\mathcal{O}(m\log n)\))。

Code

#include <bits/stdc++.h>
#define REP(i, l, r) for (int i = (l); i <= (r); ++ i)
#define DEP(i, r, l) for (int i = (r); i >= (l); -- i)
#define fi first
#define se second
#define pb emplace_back
#define mems(x, v) memset((x), (v), sizeof(x))
using namespace std;
namespace Milkcat {
	typedef long long LL;
	typedef pair<LL, LL> pii;
	const int N = 1e6 + 5;
	LL n, m, s, t, p, q, u, v, w, sd[N], td[N], pd[N], qd[N], dg[N], mn1[N], mn2[N];
	vector<pii> G[N]; vector<int> E[N];
	void dijk(int s, LL* d) {
		priority_queue<pii, vector<pii>, greater<pii>> q;
		REP(i, 1, n) d[i] = 1e18;
		d[s] = 0, q.emplace(d[s], s);
		while (q.size()) {
			auto [dis, u] = q.top(); q.pop();
			if (dis != d[u]) continue;
			for (auto [v, w] : G[u])
				if (d[v] > d[u] + w) d[v] = d[u] + w, q.emplace(d[v], v);
		}
	}
	void chkmin(LL& x, LL y) { x = min(x, y); }
	int main() {
		cin >> n >> m >> s >> t >> p >> q;
		REP(i, 1, m) cin >> u >> v >> w, G[u].pb(v, w), G[v].pb(u, w);
		dijk(s, sd), dijk(t, td), dijk(p, pd), dijk(q, qd);
		REP(u, 1, n) {
			mn1[u] = pd[u], mn2[u] = qd[u];
			for (auto [v, w] : G[u])
				if (sd[u] + td[v] + w == sd[t]) E[u].pb(v), dg[v] ++;
		}
		queue<int> q; LL rs = 1e18;
		REP(i, 1, n) if (!dg[i]) q.push(i);
		while (q.size()) {
			int u = q.front(); q.pop();
			chkmin(rs, mn1[u] + qd[u]), chkmin(rs, mn2[u] + pd[u]);
			for (int v : E[u]) {
				chkmin(mn1[v], mn1[u]), chkmin(mn2[v], mn2[u]);
				if (!-- dg[v]) q.push(v);
			}
		}
		cout << rs << '\n';
		return 0;
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int T = 1;
	while (T --) Milkcat::main();
	return 0;
}
posted @ 2024-09-03 07:03  喵仔牛奶  阅读(8)  评论(0编辑  收藏  举报