1. 程式人生 > 實用技巧 >#2350. 「JOI 2018 Final」月票購買

#2350. 「JOI 2018 Final」月票購買

先跑一遍 \(s,t,u,v\) 的單源最短路,然後可以免費的路徑是 \(s\)\(t\) 的最短路徑的連續的一段,可以反向。

考慮列舉免費路徑的開頭 \(x\), 計算出從 \(f_x, g_x\) 表示免費路徑的起點從 \(x\) 開始,\(x\)\(v, u\) 的最短距離。

\[f_x = min (dv_x, f_y), g_x = min(du_x, g_y)\]

然後答案就是 \(min(f_x + du_x, g_x + dv_x)\)\(du_v\) 的最小值。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define fi first
#define se second
const int N = 4e5 + 10, inf = 0x3f3f3f3f, mod = 1e9 + 7;
typedef pair <int, int> P;
typedef long long LL;

int n, m, s, t, cnt = 1, h[N], x, y;
bool vis[N];
struct Edge {
	int to, next, w;
} e[N];
struct Heap {
	LL v; int num;
	bool operator < (const Heap &mpc) const {
		return v > mpc.v;
	}
};
LL ds_[N], dt_[N], dx_[N], dy_[N], ans = 1e16, f[N], g[N];

void add_(int u, int v, int w) {
	e[++cnt] = (Edge) {v, h[u], w};
	h[u] = cnt;
}

void dijkstra_(int s, LL *d) {
	priority_queue <Heap> q;
	rep (i, 1, n)
		d[i] = 1e16;
	q.push((Heap) {0, s}), d[s] = 0;
	while (!q.empty()) {
		Heap cur = q.top(); q.pop();
		int u = cur.num;
		if (cur.v != d[u])
			continue;
		for (int v, i = h[u]; i; i = e[i].next) {
			v = e[i].to;
			if (d[v] > d[u] + e[i].w) {
				d[v] = d[u] + e[i].w;
				q.push((Heap) {d[v], v});
			}
		}
	}
}

void dfs_(int u) {
	if (vis[u])
		return;
	vis[u] = 1, f[u] = dy_[u], g[u] = dx_[u];
	for (int v, i = h[u]; i; i = e[i].next) {
		v = e[i].to;
		if (ds_[u] + dt_[v] + e[i].w != ds_[t])
			continue;
		dfs_(v);
		f[u] = min(f[u], f[v]), g[u] = min(g[u], g[v]);
	}
	ans = min(ans, min(f[u] + dx_[u], g[u] + dy_[u]));
}

int main() {
	scanf("%d%d%d%d%d%d", &n, &m, &s, &t, &x, &y);
	for (int u, v, w, i = 1; i <= m; i++) {
		scanf("%d%d%d", &u, &v, &w);
		add_(u, v, w), add_(v, u, w);
	}
	
	dijkstra_(s, ds_);
	dijkstra_(t, dt_);
	dijkstra_(x, dx_);
	dijkstra_(y, dy_);

	dfs_(s);

	printf("%lld\n", min(ans, dx_[y]));
	return 0;
}