D - 安全な登山ルート / Safe Climbing Route Editorial by admin
claude4.8opus-highOverview
This problem asks us to find a path from vertex \(1\) to vertex \(N\) with a total travel time of at most \(T\) such that the maximum risk of the vertices visited along the path is minimized. We can solve this using “binary search on the answer + shortest path (Dijkstra’s algorithm)”.
Analysis
Key Insight
What we want to find in this problem is the “minimization of the maximum risk along the path”. This type of “minimax” (minimizing the maximum) problem is highly compatible with binary search.
Let us consider the following decision problem:
When the upper bound of the risk is fixed to \(D\), is it possible to reach vertex \(N\) from vertex \(1\) in a travel time of at most \(T\), passing only through vertices with a risk of at most \(D\)?
This decision problem exhibits monotonicity. That is, if it is possible to reach the destination with a certain \(D\), it is always possible with any larger \(D'\) (\(D' > D\)). This is because increasing the limit only expands the set of allowed vertices and never reduces the available paths.
Conversely, if it is impossible with a certain \(D\), it is also impossible with any smaller \(D\).
Due to this monotonicity, we can use binary search to find the minimum \(D\) for which the destination is reachable.
Decision Problem for a Fixed Risk \(D\)
Fixing the risk to \(D\) imposes the constraint “vertices with a risk strictly greater than \(D\) cannot be passed”. Thus, we can:
- Disable (forbid) all vertices with a risk strictly greater than \(D\).
- Find the shortest travel time from vertex \(1\) to vertex \(N\) using only the remaining vertices.
If this shortest time is at most \(T\), we can determine that it is “feasible”. This can be computed using the standard Dijkstra’s algorithm.
Note that the starting vertex \(1\) and the destination vertex \(N\) are also subject to the risk constraint. If \(P_1 > D\) or \(P_N > D\), we cannot even start or finish the journey, so it is immediately infeasible.
Issues with a Naive Approach
Since the risk values can span a wide range (\(0 \le P_i \le 10^9\)), performing a linear search starting from \(0\), or performing a binary search directly on the integer range \([0, 10^9]\) for \(D\), would be inefficient.
In fact, the possible values of \(D\) that can be the optimal answer are restricted to the risk values \(P_i\) of the vertices themselves (since we are taking the maximum, the optimal value must coincide with the risk of some vertex). Therefore, we can collect all \(P_i\) values, sort and remove duplicates to form a list of candidate values, and then binary search over the indices of this candidate array for efficiency.
Algorithm
- Collect the risk values \(P_i\) of all vertices, sort them, and remove duplicates to create a candidate array
cand. - Perform a binary search on the indices of the candidate array.
- For each \(D = \)
cand[mid], solve the decision problem:- If \(P_1 > D\) or \(P_N > D\), it is impossible.
- Run Dijkstra’s algorithm to find the shortest travel time from vertex \(1\) to vertex \(N\) on the graph, excluding any vertices with a risk strictly greater than \(D\).
- If the shortest time is at most \(T\), it is feasible.
- The minimum
cand[mid]that is feasible is the answer.
In the binary search, we use the standard “find the minimum value that satisfies the condition” pattern: “if feasible(mid) is true, update the answer to mid and search the left side (hi = mid - 1); if false, search the right side (lo = mid + 1)”.
Complexity
Since the number of candidates is at most \(N\), the binary search takes \(O(\log N)\) steps. Each step runs Dijkstra’s algorithm, which takes \(O((N + M) \log N)\) time.
- Time Complexity: \(O((N + M) \log N \cdot \log N) = O((N+M) \log^2 N)\)
- Space Complexity: \(O(N + M)\)
Since \(N \le 2 \times 10^4\) and \(M \le 4 \times 10^4\), this approach runs well within the time limit.
Implementation Details
Preventing Overflow: Since \(W_j \le 10^9\) and the same edge might be traversed multiple times along a path, the total distance can become very large. Use
long longto represent distances, and defineINFas a sufficiently large value that does not overflow when added to other values (e.g.,LLONG_MAX / 4).Excluding Vertices in Dijkstra: When relaxing an adjacent vertex \(v\), we can naturally forbid visiting vertices with a risk greater than \(D\) by adding a check like
if (P[v] > D) continue;. Note that the start and destination vertices should be checked separately at the beginning of the function.Early Termination: In Dijkstra’s algorithm, the distance to a vertex is finalized the first time it is popped from the priority queue. Thus, we can speed up the algorithm slightly by terminating early as soon as we pop
u == N(sincedist[N]is already determined at this point).Creating Candidates: Applying
sortfollowed byunique+eraseto remove duplicates ensures that the binary search works correctly and avoids redundant search steps.Source Code
#include <bits/stdc++.h>
using namespace std;
int main(){
int N, M;
long long T;
scanf("%d %d %lld", &N, &M, &T);
vector<long long> P(N+1);
for(int i=1;i<=N;i++) scanf("%lld",&P[i]);
vector<vector<pair<int,long long>>> g(N+1);
for(int j=0;j<M;j++){
int u,v; long long w;
scanf("%d %d %lld",&u,&v,&w);
g[u].push_back({v,w});
g[v].push_back({u,w});
}
// candidate danger values
vector<long long> cand(P.begin()+1, P.end());
sort(cand.begin(), cand.end());
cand.erase(unique(cand.begin(), cand.end()), cand.end());
const long long INF = LLONG_MAX/4;
vector<long long> dist(N+1);
auto feasible = [&](long long D)->bool{
if(P[1] > D || P[N] > D) return false;
for(int i=1;i<=N;i++) dist[i]=INF;
priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<>> pq;
dist[1]=0;
pq.push({0,1});
while(!pq.empty()){
auto [d,u]=pq.top(); pq.pop();
if(d>dist[u]) continue;
if(u==N){
return dist[N] <= T;
}
for(auto [v,w]: g[u]){
if(P[v] > D) continue;
long long nd = d + w;
if(nd < dist[v]){
dist[v]=nd;
pq.push({nd,v});
}
}
}
return dist[N] <= T;
};
// binary search smallest feasible candidate
int lo=0, hi=(int)cand.size()-1, ans=hi;
while(lo<=hi){
int mid=(lo+hi)/2;
if(feasible(cand[mid])){
ans=mid;
hi=mid-1;
} else {
lo=mid+1;
}
}
printf("%lld\n", cand[ans]);
return 0;
}
This editorial was generated by claude4.8opus-high.