ICPC 2019
ICPC 2019

K. Traffic Blights

Cars! Where do they come from? Where do they go? Nobody knows. They appear where roads have been built, as if out of nowhere. Some say that no two cars are alike. Some say that if you look closely, you can see the pale ghosts of miserable humans inside them...

Updated May 21, 2026
Track ICPC
Year 2019
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 2 seconds

Cars! Where do they come from? Where do they go? Nobody knows. They appear where roads have been built, as if out of nowhere. Some say that no two cars are alike. Some say that if you look closely, you can see the pale ghosts of miserable humans inside them, trapped forever—particularly in the morning and late afternoon. What scientific eye could frame their fearful symmetry? Well, yours, hopefully. As part of your government’s Urban Traffic Control department, you are trying to write a paper on local traffic congestion. It is too dangerous to observe cars in the wild, of course, but you have been given some data on the traffic lights along your town’s Main Street, and you would like to do some theoretical calculations about how well-synchronized they are. Main Street is set out on a line, with traffic lights placed at various points along it. Each traffic light cycles between red and green with a fixed period, being red for r seconds, then green for g seconds, then red for r seconds, and so on. The values of r and g may be different for different traffic lights. At time 0, all the lights have just turned red. Assume that an “ideal” car mystically appears at the west end of Main Street at a uniformly random real-valued time in the interval [0, 2019!] (where k! is the product of the first k positive integers), driving eastwards at a slow crawl of 1 meter/second until it hits a red light. What is the probability that it will make it through all the lights without being forced to stop? If it does hit a red light, which one is it likely to hit first? Write a program to answer these questions.

Input

The first line of input contains an integer n (1 ≤ n ≤ 500), the number of traffic lights. Each of the following n lines contains three integers x, r, and g describing a traffic light, where x (1 ≤ x ≤ 105 ) is the position of the light along Main Street in meters, and r and g (0 ≤ r, g and 1 ≤ r + g ≤ 100) are the durations in seconds of the red and green portions of the light’s period (so the light is red from time 0 to r, from time r + g to 2r + g, and so on). The west end of Main Street is at position 0, and the lights are listed in order of strictly increasing position.

Output

For each of the n lights, output a line containing the probability that this light will be the first red light an “ideal” car hits. Then output a line containing the probability that an “ideal” car makes it all the way without stopping. Your answers should have an absolute error of at most 10−6 .

Sample Tests

Sample 1
Sample Input
 4
 1 2 3
 6 2 3
 10 2 3
 16 3 4
Sample Output
0.4
0
0.2
0.171428571429
0.228571428571
Sample 2
Sample Input
6
4 1 5
9 8 7
13 3 5
21 5 7
30 9 1
2019 20 0
Sample Output
0.166666666667
0.466666666667
0.150000000000
0.108333333333
0.091666666667
0.016666666667
0.000000000000

Editorial

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

Key Observations

  • If light $i$ has period $p_i=r_i+g_i$, then the whole system is periodic in the start time with period $\operatorname{lcm}(p_1,\dots,p_n)$.

  • Instead of working modulo that enormous period, fix $X = 2^3 \cdot 3^2 \cdot 5 \cdot 7 = 2520$ and split all start times by their residue modulo $X$.

  • For a fixed residue class $t \bmod X$, light $i$ is then observed only at times $t + x_i + kX$. This sequence is periodic with reduced period $q_i = p_i / \gcd(p_i, X)$.

  • By the choice of $X$, every $q_i \le 100$ is a prime power. For different primes these reduced periods are coprime; for the same prime they divide one another. Therefore the Chinese Remainder Theorem makes the different prime blocks independent.

Algorithm

  1. For every light compute its original period $p_i$ and reduced period $q_i = p_i / \gcd(p_i, X)$. Group all lights with $q_i > 1$ by the prime base of $q_i$. For one prime $p$, let $M_p$ be the largest reduced period among those lights.

  2. Fix one residue class $t_0 \in \{0,\dots,X-1\}$. For every prime block $p$, maintain which residues modulo $M_p$ are still able to pass all lights seen so far. Initially all residues are alive.

  3. Process the lights from west to east.

    • If $q_i = 1$, then within this residue class the light has a fixed color. It is either always green or always red.

    • Otherwise, compute for each residue $k \bmod q_i$ whether the car reaches this light at a green or red moment. Inside the corresponding prime block, count how many currently alive residues modulo $M_p$ project to a red residue modulo $q_i$.

    • The product structure implies that, conditioned on passing all previous lights, the residue in this prime block is uniformly distributed among the alive residues. Hence the fraction $\text{red\_count}/\text{alive\_count}$ is exactly the probability that this light is the first red light within the current class $t_0 \bmod X$.

    • Remove those red residues from the block and continue. The probability mass that survives all lights contributes to the final ``passes all lights'' answer.

    • Average the results over all $X=2520$ residue classes. enumerate

      Correctness Proof

      We prove that the algorithm returns the correct probabilities.

      Lemma 1.

      Fix a start-time residue class $t_0 \bmod X$. For light $i$, the color pattern seen at arrival times $t_0 + x_i + kX$ is periodic in $k$ with period $q_i = p_i / \gcd(p_i, X)$.

      Proof.

      Let $d=\gcd(p_i, X)$. Then increasing $k$ by $q_i = p_i/d$ changes the arrival time by $q_i X = (p_i/d)X$, which is a multiple of $p_i$, so the color repeats. No smaller period is needed for the algorithm; only periodicity matters here. □

      Lemma 2.

      Fix $t_0 \bmod X$. For any set of already processed lights, the set of $k$ values that pass all of them factors as an independent product over prime blocks of the reduced periods.

      Proof.

      Each processed light imposes a condition only on $k \bmod q_i$, and every $q_i$ is a prime power. Conditions coming from different primes involve coprime moduli, so the Chinese Remainder Theorem shows they are independent. Conditions from the same prime all live modulo powers of that prime and therefore can be represented simultaneously modulo the largest such power in that block. Hence the full feasible set is exactly a Cartesian product of per-prime feasible residue sets. □

      Lemma 3.

      While processing a fixed residue class $t_0 \bmod X$, the probability assigned by the algorithm to light $i$ is exactly the probability that light $i$ is the first red light hit within that residue class.

      Proof.

      By Lemma 2, conditioned on having passed all previous lights, each alive residue in the current prime block is equally likely. The algorithm counts exactly those alive residues that make light $i$ red, so the ratio $\text{red\_count}/\text{alive\_count}$ is the conditional probability that light $i$ is red next. Multiplying by the probability mass that has survived the previous lights gives exactly the probability that light $i$ is the first red light hit. □

      Theorem.

      The algorithm outputs the correct probability for every light and for passing all lights.

      Proof.

      For each fixed residue class $t_0 \bmod X$, Lemma 3 shows that the algorithm computes the exact probabilities of first stopping at each light and of surviving all lights. These residue classes partition all start times into $X$ equally likely classes, so averaging over all $t_0 \in \{0,\dots,X-1\}$ gives the true overall probabilities. □

      Complexity Analysis

      For one residue class $t_0$, each light is processed in $O(q_i + M_p)$ time, where both quantities are at most $100$. Thus the total running time is $O(X \cdot n \cdot 100)$, i.e. $O(2520 \cdot n \cdot 100)$, which is easily fast enough for $n \le 500$. The memory usage is $O(\sum_p M_p)$, at most a few hundred booleans.

      Implementation Notes

      • A light is treated as red exactly when the phase is in $[0, r_i)$ and green in $[r_i, p_i)$. The boundary has measure zero, so any consistent convention there gives the same probability.

      • If $q_i = 1$, then within the current class $t_0 \bmod X$ the light's color never changes, so it can be handled as a constant-color special case.

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 {

constexpr int X = 2520;

struct Light {
    int x;
    int r;
    int g;
    int p;
    int q;
    int prime;
    int block;
};

int prime_base(int x) {
    if (x <= 1) {
        return -1;
    }
    for (int p = 2; p * p <= x; ++p) {
        if (x % p == 0) {
            while (x % p == 0) {
                x /= p;
            }
            return x == 1 ? p : -1;
        }
    }
    return x;
}

void solve() {
    int n;
    cin >> n;
    vector<Light> lights(n);
    vector<int> max_power(101, 1);

    for (int i = 0; i < n; ++i) {
        cin >> lights[i].x >> lights[i].r >> lights[i].g;
        lights[i].p = lights[i].r + lights[i].g;
        int d = __gcd(X, lights[i].p);
        lights[i].q = lights[i].p / d;
        lights[i].prime = prime_base(lights[i].q);
        lights[i].block = -1;
        if (lights[i].q > 1) {
            max_power[lights[i].prime] = max(max_power[lights[i].prime], lights[i].q);
        }
    }

    vector<int> block_of_prime(101, -1);
    vector<int> mod_of_block;
    for (int p = 2; p <= 100; ++p) {
        if (max_power[p] > 1) {
            block_of_prime[p] = static_cast<int>(mod_of_block.size());
            mod_of_block.push_back(max_power[p]);
        }
    }
    for (auto& light : lights) {
        if (light.q > 1) {
            light.block = block_of_prime[light.prime];
        }
    }

    vector<long double> answer(n + 1, 0.0L);
    vector<vector<unsigned char>> alive(mod_of_block.size());
    vector<int> alive_count(mod_of_block.size());
    vector<unsigned char> good(101);

    for (int t0 = 0; t0 < X; ++t0) {
        for (int b = 0; b < static_cast<int>(mod_of_block.size()); ++b) {
            alive[b].assign(mod_of_block[b], 1);
            alive_count[b] = mod_of_block[b];
        }

        long double survive = 1.0L;
        for (int i = 0; i < n; ++i) {
            if (survive == 0.0L) {
                break;
            }

            const Light& light = lights[i];
            if (light.q == 1) {
                int when = (t0 + light.x) % light.p;
                if (when < light.r) {
                    answer[i] += survive;
                    survive = 0.0L;
                    break;
                }
                continue;
            }

            const int q = light.q;
            const int block = light.block;
            const int mod = mod_of_block[block];
            const int current_alive = alive_count[block];
            if (current_alive == 0) {
                survive = 0.0L;
                break;
            }

            for (int k = 0; k < q; ++k) {
                int when = (t0 + light.x + static_cast<int>((1LL * k * X) % light.p)) % light.p;
                good[k] = static_cast<unsigned char>(when >= light.r);
            }

            int red_count = 0;
            for (int u = 0; u < mod; ++u) {
                if (alive[block][u] && !good[u % q]) {
                    ++red_count;
                }
            }

            answer[i] += survive * static_cast<long double>(red_count) / current_alive;
            if (red_count == 0) {
                continue;
            }
            if (red_count == current_alive) {
                survive = 0.0L;
                break;
            }

            for (int u = 0; u < mod; ++u) {
                if (alive[block][u] && !good[u % q]) {
                    alive[block][u] = 0;
                }
            }
            alive_count[block] -= red_count;
            survive *= static_cast<long double>(alive_count[block]) / current_alive;
        }

        answer[n] += survive;
    }

    cout << fixed << setprecision(12);
    for (long double value : answer) {
        cout << static_cast<double>(value / X) << '\n';
    }
}

}  // namespace

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

    solve();
    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