[JOI 2020 Final] オリンピックバス 题解

Description

link

Solution

可以发现 m5×104m\leq 5\times 10^4 所以可以直接枚举 mm 来得到答案。

可以建 44 个图,两个正图,两个反图,分别是求 1i1\to i 的最短路,ini\to n 的最短路,nin\to i 的最短路,i1i\to 1 的最短路。

先对于每个图跑最短路并求出最短路径树。

如果当前反转的边是 (u,v)(u,v),如果这条边在四个图中都不是最短路径树上的边那么翻转当前边对最短路没有影响,否则就把这条边反转后跑最短路,最后取 min\min 即可。

由于最短路径树上的边最多 4(n1)4(n-1) 条,所以最多跑 4×n4\times n 次最短路,所以时间复杂度为 O(n3)O(n^3)。(这里是稠密图,所以直接不加堆优化的 dijkstra 就行)

Code

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <bits/stdc++.h>

#ifdef ORZXKR
#include <debug.h>
#else
#define debug(...) 1
#endif

#define int long long
#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)

using namespace std;

int read() {
int x = 0, f = 0; char ch = getchar();
while (ch < '0' || ch > '9') f |= ch == '-', ch = getchar();
while (ch >= '0' && ch <= '9') x = (x * 10) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}

typedef long long ll;

const int kMaxN = 205, kMaxM = 5e4 + 5;
const ll kInf = 1e18;

int n, m;
int u[kMaxM], v[kMaxM], w[kMaxM], d[kMaxM];

struct Graph {
struct Node {
int v; ll w; int id;

Node() {}
Node(int _v, ll _w, int _id) : v(_v), w(_w), id(_id) {}
~Node() {}
};

vector<Node> G[kMaxN];
int s, rv;
ll dis1[kMaxN], dis2[kMaxN], ed[kMaxN];
bool vis[kMaxN], tr[kMaxM];

void addE(int u, int v, int w, int id) {
G[u].emplace_back(v, w, id);
}
void dijkstra1() {
fill(dis1, dis1 + 1 + n, kInf), fill(vis, vis + 1 + n, 0);
dis1[s] = 0;
for (int i = 1; i <= n; ++i) {
int u = 0;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && dis1[j] < dis1[u]) {
u = j;
}
}
if (!u) break ;
vis[u] = 1;
for (auto [v, w, id] : G[u]) {
if (dis1[v] > dis1[u] + w) {
dis1[v] = dis1[u] + w, ed[v] = id;
}
}
}
for (int i = 1; i <= n; ++i) {
if (i != s && dis1[i] != kInf) tr[ed[i]] = 1;
}
}
void dijkstra2() {
fill(dis2, dis2 + 1 + n, kInf), fill(vis, vis + 1 + n, 0);
dis2[s] = 0;
for (int i = 1; i <= n; ++i) {
int u = 0;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && dis2[j] < dis2[u]) {
u = j;
}
}
if (!u) break ;
vis[u] = 1;
for (auto [v, w, id] : G[u]) {
if (id == rv) continue ;
if (dis2[v] > dis2[u] + w) {
dis2[v] = dis2[u] + w;
}
}
}
}

ll solve(int t, int rev) {
if (!tr[rev]) return dis1[t];
rv = rev;
dijkstra2();
return dis2[t];
}

} g1, g2, g3, g4; // g1, g3 正图, g2, g4 反图

signed main() {
n = read(), m = read();
g1.s = 1, g2.s = n, g3.s = n, g4.s = 1;
for (int i = 1; i <= m; ++i) {
u[i] = read(), v[i] = read(), w[i] = read(), d[i] = read();
g1.addE(u[i], v[i], w[i], i), g3.addE(u[i], v[i], w[i], i);
g2.addE(v[i], u[i], w[i], i), g4.addE(v[i], u[i], w[i], i);
}
g1.dijkstra1(), g2.dijkstra1(), g3.dijkstra1(), g4.dijkstra1();
ll ans = g1.dis1[n] + g3.dis1[1];
for (int i = 1; i <= m; ++i) {
ll dis1 = min(g1.solve(n, i), g1.solve(v[i], i) + w[i] + g2.solve(u[i], i)),
dis2 = min(g3.solve(1, i), g3.solve(v[i], i) + w[i] + g4.solve(u[i], i));
ans = min(ans, dis1 + dis2 + d[i]);
}
if (ans >= kInf) ans = -1;
cout << ans << endl;
return 0;
}

[JOI 2020 Final] オリンピックバス 题解
https://sobaliuziao.github.io/2022/10/07/post/c10a9953.html
作者
Egg_laying_master
发布于
2022年10月7日
许可协议