ICPC 2025
ICPC 2025

C. Bride of Pipe Stream

The story continues! For several years now, your town has been gifted with an abundance of Flubber, the adorable-but-slightly-flammable-and-toxic-and-acidic-and-sentient-and-mischievous man-made chemi- cal. The search continues for more (or, well, any) uses...

Updated May 21, 2026
Track ICPC
Year 2025
Statement Text + PDF
TeXC++Statement textStatement PDF

Problem Statement

Formatted from the contest statement text, with sample tests broken out into copyable blocks.

Time limit 12 seconds

The story continues! For several years now, your town has been gifted with an abundance of Flubber, the adorable-but-slightly-flammable-and-toxic-and-acidic-and-sentient-and-mischievous man-made chemi- cal. The search continues for more (or, well, any) uses for the substance. But in the meantime, the Flubber factory continues to produce it at full capacity. Efforts to shut it down have failed, partly be- cause nobody is sure who is actually running the factory. You’ve been tasked with storing the perpetually-flowing Flubber in various Flubber reservoirs for future use (or, at least, to get it out of everyone’s hair – literally). To accomplish this, you have access to a complicated network of Flubber ducts, connecting up various Flubber stations and reservoirs. Every Flubber station has one or more Flubber ducts leading from it, and has various gates that may be raised or lowered so that incoming Flubber will drain into the output Flubber ducts in any desired proportion. For instance, you can send all the Flubber down one duct, or split it between two ducts 25–75, etc. In contrast, a Flubber duct flows down to one or more lower stations or reservoirs, but the Flubber drains into them in a fixed proportion that you do not control. It is possible that some of the Flubber is lost to the environment as well, but that is a problem for your successor, not you. You would like to fill all the reservoirs as quickly as possible. That is, you want to maximize the minimum amount of Flubber flowing into any of the reservoirs, among all possible configurations of station drainage. Figure C.1 illustrates the two sample inputs. Stations and reservoirs are shown as numbered nodes, colored green for stations and blue for reservoirs. Ducts are depicted as white nodes. For example, in the first sample input (left), Flubber can be sent from station 1 in any proportion to its two downstream ducts, but each duct will distribute its inflow according to the percentages printed on its outgoing edges.

1
                                          1
40%
                        2
80%   10%    30%
                                            50%       40%       60%       50%
                            100%
3            4              5                    2                   3
Figure C.1: Illustrations of the two sample inputs.

Input

The first line of input contains three integers s, r, and d, where s (1 ≤ s ≤ 10 000) is the number of stations, r (1 ≤ r ≤ 3) is the number of reservoirs, and d (s ≤ d ≤ 20 000) is the number of ducts. The stations are numbered from 1 to s and the reservoirs are numbered from s + 1 to s + r, in decreasing order of altitude. The factory’s Flubber initially flows into station 1.

Each of the remaining d lines starts with two integers i and n, where i (1 ≤ i ≤ s) is the station that can drain into this duct, and n (1 ≤ n ≤ 10) is the number of outputs of this duct. The remainder of the line contains n pairs of integers o and p, where o (i < o ≤ s + r) is a station or reservoir to which this duct drains, and p (1 ≤ p ≤ 100) is the percentage of the Flubber entering the duct that will drain to o. The o values for a given duct are distinct. Every station has at least one duct that it can drain into. The percentages for a given duct’s outputs will sum to at most 100.

Output

Output a single percentage f , which is the highest possible percentage such that, for some configuration of station drainage, all reservoirs receive at least f % of the factory’s produced Flubber. Your answer should have an absolute error of at most 10−6 .

Sample Tests

Sample 1
Sample Input
 2   3   3
 1   2   3 80 4 10
 1   2   2 40 4 30
 2   1   5 100
Sample Output
24.0
Sample 2
Sample Input
 1   2   3
 1   1   2 50
 1   1   3 50
 1   2   2 40 3 60
Sample Output
42.8571428571

Editorial

The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.

Key Observations

  • Let $x=(x_1,\dots,x_r)$ be the vector of reservoir inflows produced by some station-splitting strategy. The set of all such vectors is convex, because every station can mix two strategies by using the corresponding convex combination of its split ratios.

  • For any weight vector $\lambda \ge 0$ with $\sum \lambda_i = 1$, maximizing $\lambda \cdot x$ is easy. If $H_i(\lambda)$ denotes the best weighted value obtainable from station $i$, then \[ H_i(\lambda)=\max_{\text{duct }d\text{ out of }i} \left(\sum_{j=1}^{r} \lambda_j \cdot \text{res}_{d,j} + \sum_{k} \text{frac}_{d,k} \cdot H_k(\lambda)\right). \]

  • The original objective is \[ \max_x \min_j x_j. \] Over a convex feasible set this is equal to \[ \min_{\lambda \ge 0,\ \sum \lambda_i=1} \max_x \lambda \cdot x. \] So we only need to minimize $H_1(\lambda)$ over the simplex.

  • Since $r \le 3$, the simplex has dimension at most $2$. The function $H_1$ is convex because it is the pointwise maximum of linear functions, so one-dimensional and nested golden-section searches are sufficient.

Algorithm

  1. Store every duct of every station as: the fixed fractions going to later stations, and the fixed fractions going directly to the reservoirs.

  2. For a fixed $\lambda$, evaluate $H_i(\lambda)$ by dynamic programming from station $s$ down to station $1$, since all edges go to larger indices.

  3. If $r=1$, the answer is just $H_1(1)$.

  4. If $r=2$, write $\lambda=(t,1-t)$ and minimize the convex function $H_1(t,1-t)$ on $[0,1]$ by golden-section search.

  5. If $r=3$, write $\lambda=(x,y,1-x-y)$ with $x,y \ge 0$ and $x+y \le 1$. For a fixed $y$, minimize over $x \in [0,1-y]$ by golden search, then minimize the resulting convex function of $y$ by another golden search.

Correctness Proof

We prove that the algorithm returns the correct answer.

Lemma 1.

For a fixed weight vector $\lambda$, the dynamic program computes the maximum possible value of $\lambda \cdot x$ obtainable from each station.

Proof.

Consider a station $i$. Once the incoming flow reaches $i$, the only decision is how to split that flow among its outgoing ducts. Because the weighted objective is linear, sending all flow through the outgoing duct with the largest downstream weighted value is optimal. The formula used by the program is exactly that best value. Processing stations in reverse order is valid because every duct goes only to larger indices, so all needed subproblems are already known. □

Lemma 2.

The optimum minimum reservoir inflow equals \[ \min_{\lambda \ge 0,\ \sum \lambda_i=1} H_1(\lambda). \]

Proof.

Let $F$ be the convex set of all feasible reservoir vectors. For any feasible $x$ and any simplex weight vector $\lambda$, we have $\min_j x_j \le \lambda \cdot x$. Therefore \[ \max_{x \in F} \min_j x_j \le \min_{\lambda} \max_{x \in F} \lambda \cdot x. \] Conversely, if $z$ is the optimal max-min value, then the set $F$ intersects the box $[z,\infty)^r$ and does not intersect the interior of any strictly larger such box. A supporting hyperplane of $F$ at an optimal point gives a simplex weight vector $\lambda$ for which $\max_{x \in F}\lambda \cdot x = z$. Hence equality holds. □

Theorem.

The algorithm outputs the correct answer for every valid input.

Proof.

By Lemma 1, for every tested $\lambda$ the program evaluates $H_1(\lambda)$ correctly. By Lemma 2, the true answer is the minimum of this function over the simplex. Because $H_1$ is convex, its restriction to any line segment is also convex, hence unimodal. Therefore the golden-section searches used by the program converge to the global minimum in the one-dimensional and nested two-dimensional cases covered by $r \le 3$. Thus the printed value is the optimal minimum reservoir inflow. □

Complexity Analysis

Let $D$ be the number of ducts. One evaluation of $H_1(\lambda)$ costs $O(D \cdot r)$, which is $O(D)$ because $r \le 3$. The search performs only a fixed number of evaluations, so the total running time is $O(D)$ up to a constant factor, and the memory usage is $O(D)$.

Implementation Notes

  • The output is a percentage, so the program multiplies the final fraction by $100$.

  • Using about $80$--$200$ golden-search iterations is easily enough for the required $10^{-6}$ absolute error.

Code

C++ solution used for this page.

C++

Clean code view with a raw-file link when you want the original source.

Raw file
#include <bits/stdc++.h>
using namespace std;

namespace {

struct Pipe {
    vector<pair<int, double>> station_edges;
    vector<double> reservoir_edges;
};

int s, r, d;
vector<vector<Pipe>> pipes;

double value_for_weights(const vector<double>& w) {
    vector<double> best(s + 1, 0.0);
    for (int u = s; u >= 1; --u) {
        double cur = -1e100;
        for (const Pipe& pipe : pipes[u]) {
            double cand = 0.0;
            for (int i = 0; i < r; ++i) {
                cand += w[i] * pipe.reservoir_edges[i];
            }
            for (const auto& edge : pipe.station_edges) {
                cand += edge.second * best[edge.first];
            }
            cur = max(cur, cand);
        }
        best[u] = cur;
    }
    return best[1];
}

double golden_min(const function<double(double)>& f, double lo, double hi, int iters) {
    const double PHI = (sqrt(5.0) - 1.0) / 2.0;
    double x1 = hi - PHI * (hi - lo);
    double x2 = lo + PHI * (hi - lo);
    double y1 = f(x1);
    double y2 = f(x2);
    for (int it = 0; it < iters; ++it) {
        if (y1 > y2) {
            lo = x1;
            x1 = x2;
            y1 = y2;
            x2 = lo + PHI * (hi - lo);
            y2 = f(x2);
        } else {
            hi = x2;
            x2 = x1;
            y2 = y1;
            x1 = hi - PHI * (hi - lo);
            y1 = f(x1);
        }
    }
    return min(y1, y2);
}

}  // namespace

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> s >> r >> d;
    pipes.assign(s + 1, {});
    for (int i = 0; i < d; ++i) {
        int at, cnt;
        cin >> at >> cnt;
        Pipe pipe;
        pipe.reservoir_edges.assign(r, 0.0);
        for (int j = 0; j < cnt; ++j) {
            int to, p;
            cin >> to >> p;
            double frac = p / 100.0;
            if (to <= s) {
                pipe.station_edges.push_back({to, frac});
            } else {
                pipe.reservoir_edges[to - s - 1] += frac;
            }
        }
        pipes[at].push_back(move(pipe));
    }

    cout << fixed << setprecision(10);
    if (r == 1) {
        cout << 100.0 * value_for_weights(vector<double>(1, 1.0)) << '\n';
        return 0;
    }

    if (r == 2) {
        auto f = [&](double x) {
            vector<double> w = {x, 1.0 - x};
            return value_for_weights(w);
        };
        cout << 100.0 * golden_min(f, 0.0, 1.0, 200) << '\n';
        return 0;
    }

    auto inner = [&](double y) {
        auto g = [&](double x) {
            vector<double> w = {x, y, 1.0 - x - y};
            return value_for_weights(w);
        };
        return golden_min(g, 0.0, max(0.0, 1.0 - y), 80);
    };

    cout << 100.0 * golden_min(inner, 0.0, 1.0, 80) << '\n';
    return 0;
}

Source Files and Assets

Raw files are still available here when you want the original TeX, C++, or statement assets.

Show raw files