Quick Overview

Points land one at a time on the segment from 0 to 50, each contaminating an interval of length 1 around it; after each landing, report whether the whole segment is contaminated. Tests interval union maintenance, exact boundary handling and efficient updates.

Is the Segment From 0 to 50 Fully Contaminated After Each Landing Point?

Company: Waymo

Role: Site Reliability Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

Consider the number line segment `[0, 50]`. Points keep landing on it one at a time. Each point contaminates the region within 0.5 of where it lands, that is, an interval of length 1 centered on the point. Once part of the line is contaminated it stays contaminated forever. Points can land anywhere on the segment, and the regions of different points may overlap. The interview asked you to design a function that is called once per landing point (given as a floating-point number) and returns a boolean telling whether the entire segment `[0, 50]` is now contaminated. In this console version you receive all landing points in order and return the answer after each one. ### Function Signature ```python def fully_contaminated_after_each(points: list[float]) -> list[bool]: ``` ### Rules - A point landing at `p` contaminates the closed interval `[p - 0.5, p + 0.5]`, clipped to `[0, 50]`. - The segment is fully contaminated when every real number in `[0, 50]`, including both endpoints, lies in at least one contaminated interval. Two intervals that touch at a single point leave no gap between them. - The `i`-th output is `True` if the segment is fully contaminated after processing `points[0]` through `points[i]`, and `False` otherwise. Once an output is `True`, all later outputs are `True`. - Every landing point has at most two digits after the decimal point. Decide coverage using the exact decimal values as written (for example, `0.35 + 0.5` equals exactly `0.85`), not values affected by floating-point rounding. ### Constraints - `1 <= len(points) <= 10^5` - `0 <= points[i] <= 50` - Each `points[i]` has at most two digits after the decimal point. - Points may repeat. ### Examples **Example 1** - Input: `points = [25.0, 10.3]` - Output: `[False, False]` - Explanation: Only `[24.5, 25.5]` and `[9.8, 10.8]` are contaminated. **Example 2** - Input: `points` is the 49 values `0.5, 1.5, 2.5, ..., 48.5` in increasing order, followed by `49.6`, followed by `49.5` (51 points in total). - Output: 49 values `False`, then `False`, then `True`. - Explanation: The first 49 points contaminate `[0, 49]` exactly. The point `49.6` adds `[49.1, 50]` (clipped at 50), which still leaves the gap between 49 and 49.1. The point `49.5` adds `[49, 50]` and closes it. **Example 3** - Input: `points = [0.2, 49.8]` - Output: `[False, False]` - Explanation: The contaminated intervals `[0, 0.7]` and `[49.3, 50]` cover both ends, but the middle is still clean.

Overview: Points land one at a time on the segment from 0 to 50, each contaminating an interval of length 1 around it; after each landing, report whether the whole segment is contaminated. Tests interval union maintenance, exact boundary handling and efficient updates.

The number line segment `[0, 50]` starts completely clean. Points land on it one at a time. A point landing at `p` contaminates every position within `0.5` of it, that is, the closed interval `[p - 0.5, p + 0.5]` clipped to `[0, 50]`. Contamination is permanent, points may land anywhere on the segment, and the intervals of different points may overlap. In an interview this is a function called once per landing point that reports whether the whole segment is now contaminated. In this console version you receive all landing points in arrival order and return the answer after each one. Implement `fully_contaminated_after_each(points)` that returns a list of booleans of the same length as `points`. ### Rules - A point landing at `p` contaminates the closed interval `[p - 0.5, p + 0.5]`, clipped to `[0, 50]`. - The segment is fully contaminated when every real number in `[0, 50]`, including both endpoints `0` and `50`, lies in at least one contaminated interval. Two intervals that touch at a single point leave no gap between them. - The `i`-th output is `True` if the segment is fully contaminated after processing `points[0]` through `points[i]`, and `False` otherwise. Once an output is `True`, all later outputs are `True`. - Every landing point has at most two digits after the decimal point. Decide coverage using the exact decimal values as written (for example, `0.35 + 0.5` equals exactly `0.85`, the same value as `1.35 - 0.5`), not values affected by floating-point rounding. ### Constraints - `1 <= len(points) <= 10^5` - `0 <= points[i] <= 50` - Each `points[i]` has at most two digits after the decimal point. - Points may repeat. ### Example 1 - Input: `points = [25.0, 10.3]` - Output: `[False, False]` - Explanation: only `[24.5, 25.5]` and `[9.8, 10.8]` are contaminated. ### Example 2 - Input: `points` is the 49 values `0.5, 1.5, 2.5, ..., 48.5` in increasing order, followed by `49.6`, followed by `49.5` (51 points in total). - Output: 49 values `False`, then `False`, then `True`. - Explanation: the first 49 points contaminate `[0, 49]` exactly. The point `49.6` adds `[49.1, 50]` (clipped at 50), which still leaves the gap between `49` and `49.1`. The point `49.5` adds `[49, 50]` and closes it. ### Example 3 - Input: `points = [0.2, 49.8]` - Output: `[False, False]` - Explanation: the intervals `[0, 0.7]` and `[49.3, 50]` cover both ends, but the middle is still clean.

Constraints

  • 1 <= len(points) <= 10^5
  • 0 <= points[i] <= 50
  • Each points[i] has at most two digits after the decimal point
  • Points may repeat

Examples

Input: ([25.0, 10.3],)

Expected Output: [False, False]

Explanation: Source example 1: two isolated intervals leave most of the segment clean.

Input: ([0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, 25.5, 26.5, 27.5, 28.5, 29.5, 30.5, 31.5, 32.5, 33.5, 34.5, 35.5, 36.5, 37.5, 38.5, 39.5, 40.5, 41.5, 42.5, 43.5, 44.5, 45.5, 46.5, 47.5, 48.5, 49.6, 49.5],)

Expected Output: [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True]

Explanation: Source example 2: [0, 49] covered, 49.6 leaves the 0.1 gap (49, 49.1), 49.5 closes it on the last point.

Hints

  1. Multiply every value by 100. Each interval then has integer endpoints between 0 and 5000, and the exact-decimal rule becomes plain integer arithmetic with no rounding questions.
  2. With integer endpoints, [0, 5000] is fully covered exactly when each of the 5000 unit pieces [k, k + 1] lies inside some interval. Keep a count of how many pieces are still clean.
  3. Each piece only needs to be marked once. A 'next clean piece' pointer array with path compression lets every new interval jump over pieces that are already contaminated.

Loading coding console...

Show the approach

Approach

Scale every landing point by 100 and round to the nearest integer. Because each value has at most two decimals and lies in [0, 50], this recovers its exact value in hundredths, p, with no floating-point error. The point then contaminates the integer interval [max(0, p - 50), min(5000, p + 50)].

Split the segment [0, 5000] (in hundredths) into 5000 unit pieces [k, k + 1] for k = 0..4999. A closed interval with integer endpoints either contains a whole piece or touches it only at an endpoint, so the union of the intervals covers every real number in [0, 5000] exactly when every piece is contained in some interval: if piece k is uncovered, its midpoint k + 0.5 is uncovered, and if every piece is covered, every real number (endpoints included) lies in a covered piece. Touching intervals such as [0, 85] and [85, 185] therefore need no special handling, and a gap as small as 0.01 is exactly one clean piece.

The reference keeps remaining, the number of clean pieces, and an array nxt where following nxt from k reaches the first clean piece at or after k (a union-find over 'next clean position', with index 5000 as a sentinel). For an interval [a, b] it jumps to the first clean piece at or after a, marks it contaminated by linking it to k + 1, decrements remaining, and repeats until the next clean piece is at or beyond b. Path compression keeps the jumps short. After each point the answer is simply remaining == 0, which stays True forever once reached.

Each of the 5000 pieces is marked at most once, and each point performs one find plus one find per newly marked piece, so the total work is linear in the number of points plus the fixed segment resolution.

Time complexity:
O(n + L * alpha(L)), where n = len(points) and L = 5000 is the segment length in hundredths; linear in n for the fixed segment
Space complexity:
O(n + L) for the output list and the next-clean-piece array