This problem is somewhere in my top-5 problems for 2025. The solution is surprisingly simple and intuitive for a 3200 rated problem.
Problem CF-2128F
Statement
You are given an undirected connected graph with
nodes and edges. The weight wi of the -th edge is not yet decided and must be a real number between and . You are given a node
. Determine if there exists a valid assignment of weights such that:
for all
Constraints:
-
-
-
-
no self-loops
-
Solution
It's easy to see that the problem requires us to check if there exists an assignment of weights for which the node
Let's consider some shortest path in a valid assignment of weights
Observation 1:
For all
, we can set without affecting the validity of .
Proof
Doing so doesn't affect the weight of
Observation 2:
For all
, we can set without affecting the validity of .
Proof
Consider a single change, on edge
-
Paths that don't contain the edge
: they were worse than , and remain so. -
Paths that contain the edge
once: they were initially worse than , and their total edge weight is reduced by the same amount as , so they remain worse. -
Paths that contain the edge
multiple times: while they receive a greater decrease to their total weight than , simply cannot be the shortest paths from to as they're not simple, and all edge weights are strictly positive.
A vague skeleton for a potential solution begins to take form: maybe we fix some simple path from
-
It's the shortest path from
to . -
Its total weight is strictly smaller than that of the shortest path from
to through .
We can observe that the first condition is actually unnecessary: it suffices to check for the second condition, because the second being satisfied implies that there's some path from
Let us now look at the intersection between
This leap might feel a bit arbitrary to the passive reader, so the intuitive justification I provide is that since we reach nodes on
Let the vertices on
Observation 3:
There exists some
(shortest path from to through ) such that the sequence takes the following form: (ie. a non-empty segment of s, followed by a non-empty segment of s, followed by a non-empty segment of s).
Proof
-
as . -
as . -
There exists some
such that , and as . -
Let us now consider some shortest path from
to through , and show that we can transform to while still having the same weight.The first three points imply that the sequence
looks like (with the guaranteed at position existing due to the guaranteed (and singular) existence of in ). Now consider the closest s to to the left and right (both are guaranteed to exist as the first and last elements are s). Let their position be and respectively. Let the -th and -th nodes in be and respectively.Let's now construct the following path,
:-
We go from
to through the same sub-path we do on (note that is guaranteed to exist in ). -
We go from
to through the same sub-path we do on (note that this ensures we go through ). -
We go from
to through the same sub-path we do on (note that is guaranteed to exist in ).
Claim 3.1:
The total weight of
is not greater than that of .Proof:
Consider the three subpaths that both
and can be divided into: . The second sub-path ( ) is the same in both and , and therefore has an equal contribution to the weight. Now, since is one of the shortest paths from to , the sub-paths and on are the shortest paths between those nodes, and therefore cannot have greater weights than their counter-parts in .Claim 3.2:
takes the following form: (ie. a non-empty segment of 1s, followed by a non-empty segment of 0s, followed by a non-empty segment of 1s).Proof:
Obvious.
We have therefore showed the existence of a valid
. -
Fuck, I now feel stupid for putting the proof above into a spoiler as I'm going to be using terms defined therein. Just read it if you haven't.
Anyways, how does all of this even help us? It helps us because it implies that if there exists a valid assignment of weights, then there exists a pair of paths
is the shortest path from to . is the shortest path from to through , and satisfies that nice property we derived earlier (follows , only deviates once to visit , and then follows again). has a smaller total weight than .
This is extremely convenient as we can now look at the smallest path from
Let's now define
We observe that for a fixed
The only point of contention one may have with this formula is that for certain
Observation 4:
The minimum for
is produced by at least one pair for which both and are valid.
Proof
As we've already shown earlier, some optimal path through
Let's simplify things a bit for us now.
Observation 5
If
is satisfied for any path without , then the weight assignment induced by is valid.
Proof
How does this relate to our original requirement of having
Alright, with all of that foreplay out of the way, how do we design an efficient algorithm that exploits these properties? All we care about is finding a path
Consider a simple line graph from
Consider the following pseudo-code that does this:
p = nodes on the path from 1 to n
sat = true
for i from 1 to n:
v = p[i]
for j from 1 to i:
u = p[j]
if dk[u] + dk[v] <= dis_P(u, v):
sat = false
This works in
This leads to the following
p = nodes on the path from 1 to n
sat = true
worst_suffix = -dk[p[1]]
for i from 2 to n:
v = p[i]
worst_suffix = max(worst_suffix + dis_P(p[i - 1], v), -dk[v])
if dk[v] <= worst_suffix:
sat = false
Alright, let's now consider doing this for arbitrary paths in a general graph.
We will try to incrementally build good paths from node
- It must not contain
. must obviously be satisfied for on this prefix path.- In addition, note that as long as
is satisfied on this prefix, the only thing that will affect as we further extend this path is the analogous value ofworst_suffixuntil now. It's easy to see that we should aim to minimise this value.
This trivially leads to the following
precompute dk using djikstras
opt[n]
opt[1] = -dk[1]
for u from 2 to n:
opt[u] = inf
for round from 1 to n:
nopt = opt
for u from 1 to n:
for [v, l, r] in adj[u]:
if (u != k and v != k and dk[v] > opt[u] + l)
nopt[v] = min(nopt[v], max(-dk[v], opt[u] + l))
opt = nopt
if opt[n] != inf:
valid assignment exists
To optimize this to