Official
D - 安全な登山ルート / Safe Climbing Route Editorial
by
D - 安全な登山ルート / Safe Climbing Route Editorial
by
MMNMM
危険度が \(X\) 以下であるような地点だけを通って地点 \(1\) から地点 \(N\) まで行くときにかかる最短の所要時間を求めることができれば、これを \(O(\log N)\) 回繰り返すことで元の問題を解くことができます。
上の問題は、危険度が \(X\) 以下の地点どうしを結ぶ辺のみを使って Dijkstra 法などを行うことで \(O((N+M)\log N)\) 時間で解くことができます。
よって、全体で \(O((N+M)(\log N)^2)\) 時間でこの問題を解くことができ、これは十分高速です。
実装例は以下のようになります。
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
int main() {
int N, M, T;
cin >> N >> M >> T;
vector<int> P(N);
for (int& p : P) {
cin >> p;
}
vector<vector<tuple<int, int>>> edges(N);
for (int i = 0; i < M; ++i) {
int u, v, w;
cin >> u >> v >> w;
--u;
--v; // 0-indexed にしておく
edges[u].emplace_back(v, w);
edges[v].emplace_back(u, w);
}
// 危険度が X 以下の地点だけを使って地点 1 から地点 N まで移動するとき、所要時間が T を越えてしまうか判定する関数
auto check = [N, T, P, &edges](int X) {
// 地点 1 の危険度が X を越えていたらダメ
if (P[0] > X) return true;
// Dijkstra 法で所要時間を求める
vector distance(N, N * 1000000001l);
priority_queue<pair<long, int>, vector<pair<long, int>>, greater<>> pq;
pq.emplace(distance[0] = 0, 0);
while (!pq.empty()) {
auto [d, now] = pq.top();
pq.pop();
if (distance[now] < d) {
continue;
}
if (now == N - 1) { // 地点 N に到達したら
return d > T; // 所要時間が T 以下か判定
}
for (auto [next, weight] : edges[now]) {
// 危険度が X 以下の頂点だけ遷移を行う
if (P[next] <= X && distance[next] > d + weight) {
pq.emplace(distance[next] = d + weight, next);
}
}
}
return true;
};
// ソートして二分探索
ranges::sort(P);
cout << *ranges::partition_point(P, check) << endl;
return 0;
}
from heapq import heappush, heappop
N, M, T = map(int, input().split())
P = list(map(int, input().split()))
edges = [[] for i in range(N)]
for i in range(M):
u, v, w = map(int, input().split())
u -= 1
v -= 1 # 0-indexed にしておく
edges[u].append((v, w))
edges[v].append((u, w))
# 危険度が X 以下の地点だけを使って地点 1 から地点 N まで移動するとき、所要時間が T を越えてしまうか判定する関数
def check(X):
# 地点 1 の危険度が X を越えていたらダメ
if P[0] > X:
return False
# Dijkstra 法で所要時間を求める
distance = [N * 1000000001 for i in range(N)]
distance[0] = 0
pq = []
heappush(pq, (0, 0))
while len(pq) > 0:
dist, now = heappop(pq)
if distance[now] < dist:
continue
if now == N - 1: # 地点 N に到達したら
return dist <= T # 所要時間が T 以下か判定
for next, weight in edges[now]:
# 危険度が X 以下の頂点だけ遷移を行う
if P[next] <= X and distance[next] > dist + weight:
distance[next] = dist + weight
heappush(pq, (dist + weight, next))
# 二分探索で答えを求める
L = -1
R = 1000000001
while L + 1 < R:
M = (L + R) // 2
if check(M):
R = M
else:
L = M
print(R)
