Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
The triangle inequality always gives . So the only way to win is strict: make some route shorter than every route that goes through .
If some assignment works, you can assume a very extreme assignment works: pick one shortest path , set every edge on to its lower bound , and set every other edge to its upper bound .
Now fix this green path . For two vertices appearing in this order on , the segment of from to must be strictly shorter than going using upper-bound distances. Otherwise a shortest route through can tie you, and equality comes back like a bad sequel.
Compute with one normal Dijkstra using edge weights . While walking along with lower weights, the condition for every earlier vertex becomes: current elapsed time minus the earliest possible “warning time” must stay .
Store one value per vertex: the minimum possible danger timer . Start at with . Traversing edge with lower weight changes it to . Process states by increasing like Dijkstra, and a state is usable only if . Reaching means YES.
The condition in the statement looks like equality, but because shortest paths obey triangle inequality, it is really asking for
So we need to make every shortest path avoid in a strict way. No ties allowed. Ties are death here.
Step 1: push weights to the extremes
Suppose some valid assignment works. Let be a shortest path from to under that assignment.
We can transform the assignment like this:
This keeps the answer working.
Why? If we decrease an edge on , then the chosen shortest distance decreases by exactly that amount. Meanwhile, can decrease by at most that amount. Intuitively, an edge of a simple path cannot be useful in both halves through in a way that gives a double discount; shortest-path geometry blocks that nonsense.
If we increase an edge outside , then the path is untouched, so does not increase, and the through- distance cannot decrease.
Therefore, we only need to decide whether there exists a path such that setting to lower bounds and all other edges to upper bounds gives strict inequality.
Step 2: characterize a good path
Fix a candidate path from to . Let be the lower-bound length of the segment of from to , where appears before on .
Also define
where every edge has weight .
The path is good exactly when for every pair on with before ,
The right side is the best possible upper-bound cost of going from to and then from to .
Why is this condition necessary? If for some we had
then the segment of from to could be replaced by a route through of no larger length. That creates a shortest path through , so equality holds. Bad.
Why is it sufficient? If a shortest path through existed, look at the last vertex of it touches before reaching , and the first vertex of it touches after leaving . Between those two vertices, the through- route would be no longer than the corresponding segment of , violating the condition above.
So the whole problem becomes: find a path from to whose every segment beats the cops going through .
Step 3: the robber interpretation
Think of yourself as a robber walking from to .
For a city , the warning time from to is .
At your current city, define a timer :
You are safe at vertex exactly when
If , the cops have had enough time to get from to , so you are caught. Skill issue, but for the path, not for you.
Step 4: timer transition
At vertex with timer , traverse edge with lower weight .
During the move, time increases by , so the old warning timer becomes .
But when you arrive at , a new citizen from can warn the cops after seconds. Relative to the current time, that new warning timer is .
The cops use the earliest warning, which corresponds to the larger timer, so
This is the whole trick.
We want the minimum possible timer for each vertex. Smaller timer means safer. Since every transition strictly increases time by a positive edge length, we can process states in increasing timer order, just like Dijkstra.
Algorithm:
YES; otherwise NO.The complexity is
per total graph size, which is fine because the sums of and are both bounded by .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Edge {
int to;
ll l, r;
};
int main() {
setIO();
int T;
cin >> T;
const ll INF = (1LL << 62);
while (T--) {
int n, m, k;
cin >> n >> m >> k;
vector<vector<Edge>> g(n + 1);
for (int i = 0; i < m; i++) {
int u, v;
ll l, r;
cin >> u >> v >> l >> r;
g[u].push_back({v, l, r});
g[v].push_back({u, l, r});
}
vector<ll> d(n + 1, INF);
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;
d[k] = 0;
pq.push({0, k});
while (!pq.empty()) {
auto [du, u] = pq.top();
pq.pop();
if (du != d[u]) continue;
for (const Edge &e : g[u]) {
if (d[e.to] > du + e.r) {
d[e.to] = du + e.r;
pq.push({d[e.to], e.to});
}
}
}
vector<ll> best(n + 1, INF);
vector<char> done(n + 1, false);
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> q;
best[1] = -d[1];
q.push({best[1], 1});
bool ok = false;
while (!q.empty()) {
auto [t, u] = q.top();
q.pop();
if (t != best[u] || done[u]) continue;
if (t >= d[u]) continue;
done[u] = true;
if (u == n) {
ok = true;
break;
}
for (const Edge &e : g[u]) {
ll nt = max(t + e.l, -d[e.to]);
if (nt < best[e.to]) {
best[e.to] = nt;
q.push({nt, e.to});
}
}
}
cout << (ok ? "YES" : "NO") << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Edge {
int to;
ll l, r;
};
int main() {
setIO();
int T;
cin >> T;
const ll INF = (1LL << 62);
while (T--) {
int n, m, k;
cin >> n >> m >> k;
vector<vector<Edge>> g(n + 1);
for (int i = 0; i < m; i++) {
int u, v;
ll l, r;
cin >> u >> v >> l >> r;
g[u].push_back({v, l, r});
g[v].push_back({u, l, r});
}
vector<ll> d(n + 1, INF);
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;
d[k] = 0;
pq.push({0, k});
while (!pq.empty()) {
auto [du, u] = pq.top();
pq.pop();
if (du != d[u]) continue;
for (const Edge &e : g[u]) {
if (d[e.to] > du + e.r) {
d[e.to] = du + e.r;
pq.push({d[e.to], e.to});
}
}
}
vector<ll> best(n + 1, INF);
vector<char> done(n + 1, false);
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> q;
best[1] = -d[1];
q.push({best[1], 1});
bool ok = false;
while (!q.empty()) {
auto [t, u] = q.top();
q.pop();
if (t != best[u] || done[u]) continue;
if (t >= d[u]) continue;
done[u] = true;
if (u == n) {
ok = true;
break;
}
for (const Edge &e : g[u]) {
ll nt = max(t + e.l, -d[e.to]);
if (nt < best[e.to]) {
best[e.to] = nt;
q.push({nt, e.to});
}
}
}
cout << (ok ? "YES" : "NO") << '\n';
}
}