CF 104479L - Lying Faces

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.

CF 104479L - Lying Faces

Rating: -
Tags: -
Solve time: 1m 57s
Verified: yes

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.

  1. Fix a candidate median value X. We classify each edge as +1 if w ≥ X, otherwise -1. This converts the problem into maximizing a score along a walk.
  2. We define a DP where dp[t][v] is the maximum score achievable at vertex v using exactly t edges. Since k ≤ 50, we only need to consider paths up to length 2k, because a longer walk can always be truncated or compressed while preserving median feasibility.
  3. Initialize all dp states for length 0 as 0 at all vertices.
  4. Transition by relaxing edges: for each step, update dp[t+1][v] = max(dp[t+1][v], dp[t][u] + value(u→v)).
  5. After filling up to length 2k, we check whether there exists any t ≥ k and vertex v such that the score condition implies at least half of the edges are good. This translates into dp[t][v] ≥ 0.
  6. If such a state exists, the candidate X is 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.