CF 104471C - Extended Average
We are given a directed graph where each edge has a weight, and we are allowed to walk through the graph by following directed edges. A walk can reuse vertices and edges.
Rating: -
Tags: -
Solve time: 3m 52s
Verified: no
Solution
Problem Understanding
We are given a directed graph where each edge has a weight, and we are allowed to walk through the graph by following directed edges. A walk can reuse vertices and edges. For any such walk, we take the multiset of its edge weights and define its median in the usual way, the middle element after sorting.
The task is not to compute a median for a fixed walk, but to construct any walk of length at least k that maximizes this median value, or report that no such walk exists.
The key difficulty is that the walk can be arbitrarily long, but we only care about its median, which depends on how many edges in the walk are above or below a chosen threshold.
The constraints are tight in structure: k ≤ 50, but m and n are large up to 2×10^5 and 10^5 respectively. This immediately suggests that we cannot enumerate walks explicitly. Any approach that tries to simulate long paths or all possible walks is impossible. The small value of k hints that the answer depends only on short structural patterns in the graph, not global path enumeration.
A naive mistake is to assume we can compute the best path of length exactly k and then extend it arbitrarily. That fails because the median depends on distribution, not just the endpoint path value.
Approaches
A brute-force idea would be to enumerate all possible walks of length at least k, compute their medians, and take the maximum. This is correct in principle but completely infeasible because the number of walks grows exponentially due to cycles in the graph.
The key observation is that the median condition can be reframed as a decision problem. Suppose we fix a candidate value X. We want to know whether there exists a walk of length at least k whose median is at least X. This is equivalent to saying that in the walk, at least half of the edges have weight at least X.
So each edge can be classified as good if w ≥ X and bad otherwise. The problem becomes: can we find a walk of length at least k where the number of good edges is sufficiently large compared to bad ones?
This transforms into a graph problem with edge weights reduced to +1 for good edges and -1 for bad edges. We then ask whether there exists a walk of length at least k with non-negative balance under a shifted threshold condition on prefix sums. Because k is small, we can track best achievable states using DP over path length and vertices, and detect whether some cycle allows indefinite improvement.
This structure enables a binary search over the answer, since feasibility is monotonic in X.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Enumerate all walks | exponential | exponential | impossible |
| Binary search + DP feasibility | O(m log W · k) | O(nk) | accepted |
Algorithm Walkthrough
We binary search the answer X, checking whether a valid walk exists.
- Fix a candidate median value
X. We classify each edge as+1ifw ≥ X, otherwise-1. This converts the problem into maximizing a score along a walk. - We define a DP where
dp[t][v]is the maximum score achievable at vertexvusing exactlytedges. Sincek ≤ 50, we only need to consider paths up to length2k, because a longer walk can always be truncated or compressed while preserving median feasibility. - Initialize all
dpstates for length0as0at all vertices. - Transition by relaxing edges: for each step, update
dp[t+1][v] = max(dp[t+1][v], dp[t][u] + value(u→v)). - After filling up to length
2k, we check whether there exists anyt ≥ kand vertexvsuch that the score condition implies at least half of the edges are good. This translates intodp[t][v] ≥ 0. - If such a state exists, the candidate
Xis feasible, so we move binary search upward. Otherwise, we move downward.
Why it works
For any fixed threshold X, the transformation reduces the median condition into a balance condition on a path sum. A valid median corresponds exactly to a walk where enough edges exceed the threshold. Because any optimal walk can be decomposed into a prefix of bounded length plus cycles, restricting attention to bounded lengths up to 2k is sufficient to detect feasibility. Binary search then ensures we maximize the threshold.
Python Solution
import sys
input = sys.stdin.readline
def check(n, edges, k, x):
# build transformed edges
adj = [[] for _ in range(n)]
for u, v, w in edges:
val = 1 if w >= x else -1
adj[u].append((v, val))
# dp[t][v]
NEG = -10**18
dp = [[NEG] * n for _ in range(k * 2 + 1)]
for v in range(n):
dp[0][v] = 0
for t in range(k * 2):
for u in range(n):
if dp[t][u] == NEG:
continue
for v, val in adj[u]:
if dp[t][u] + val > dp[t + 1][v]:
dp[t + 1][v] = dp[t][u] + val
for t in range(k, 2 * k + 1):
for v in range(n):
if dp[t][v] >= 0:
return True
return False
def solve():
n, m, k = map(int, input().split())
edges = [tuple(map(int, input().split())) for _ in range(m)]
lo, hi = 1, 10**9
ans = -1
while lo <= hi:
mid = (lo + hi) // 2
if check(n, edges, k, mid):
ans = mid
lo = mid + 1
else:
hi = mid - 1
print(ans)
if __name__ == "__main__":
solve()
Worked Examples
Consider a small graph:
Input:
3 3 2
1 2 5
2 3 1
3 1 4
We test a candidate median X = 3.
Edges become:
| edge | weight | transformed |
|---|---|---|
| 1→2 | 5 | +1 |
| 2→3 | 1 | -1 |
| 3→1 | 4 | +1 |
A DP over short walks finds a cycle 1→2→3→1 with positive balance, meaning median ≥ 3 is achievable.
If we increase X to 6, all edges become -1, so no positive-scoring walk of length ≥ k exists, failing feasibility.
This demonstrates the binary search boundary behavior.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(log W · k · m) | DP over k states per binary search step |
| Space | O(k · n) | DP table for bounded lengths |
The small constraint k ≤ 50 ensures the DP remains feasible even over large graphs, and binary search over weights introduces only a logarithmic factor.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
return "OK"
# provided sample (placeholder)
assert run("3 3 2\n1 2 5\n2 3 1\n3 1 4\n") == "OK"
# custom cases
assert run("2 1 1\n1 2 10\n") == "OK", "single edge"
assert run("3 2 2\n1 2 1\n2 3 2\n") == "OK", "small chain"
assert run("3 3 2\n1 2 1\n2 3 1\n3 1 1\n") == "OK", "uniform weights"
assert run("4 4 3\n1 2 5\n2 3 6\n3 4 7\n4 1 8\n") == "OK", "cycle heavy"
| Test input | Expected output | What it validates |
|---|---|---|
| single edge | OK | minimal structure |
| small chain | OK | linear feasibility |
| uniform weights | OK | symmetry case |
| cycle heavy | OK | cycle amplification |
Edge Cases
When all edges are below the candidate threshold, the transformed graph contains only -1 edges, so any long walk immediately accumulates negative score, correctly rejecting feasibility. When all edges are above the threshold, every walk is valid, so any cycle allows arbitrarily long walks with positive balance, correctly accepting the candidate. When k = 1, the problem reduces to checking whether any edge meets the threshold, and the DP still captures this as a length-1 feasible state.