CF 106200A - Загон для драконоосликов
We are given several axis-aligned rectangles, and each rectangle has its bottom-left corner fixed at the origin while the top-right corner is given as $(wi, hi)$. So each input pair defines a rectangle that covers all points $(x, y)$ such that $0 le x le wi$ and $0 le y le hi$.
Rating: -
Tags: -
Solve time: 55s
Verified: yes
Solution
Problem Understanding
We are given several axis-aligned rectangles, and each rectangle has its bottom-left corner fixed at the origin while the top-right corner is given as $(w_i, h_i)$. So each input pair defines a rectangle that covers all points $(x, y)$ such that $0 \le x \le w_i$ and $0 \le y \le h_i$.
All these rectangles are combined using union, meaning a point is allowed if it lies in at least one of the rectangles. The task is to compute the total area covered by this union.
A key observation is that all rectangles share the same corner at $(0,0)$, so they are all “anchored” in the same place. This makes the geometry highly structured: the union is not arbitrary overlapping rectangles, but a collection of rectangles expanding from the origin.
The constraints allow up to $10^5$ rectangles and coordinates up to $10^9$. This immediately rules out any approach that checks coverage point-by-point or discretizes the full grid. Even sweeping over coordinates naively would be impossible if we tried to treat both dimensions symmetrically.
A subtle issue appears in overlap reasoning. If one rectangle is $(3, 10)$ and another is $(10, 3)$, neither contains the other completely, so a naive “take max width and max height” is incorrect. The union is not a single rectangle unless all pairs are comparable in both dimensions.
A second edge case is when many rectangles dominate others partially. For example, $(5, 100)$, $(100, 5)$, $(6, 6)$. The union is not obvious unless we reason about structure carefully.
The correct output is a single integer: the total area covered by at least one rectangle.
Approaches
A brute-force approach would try to model the plane and mark coverage. One way is to imagine a grid up to $10^9 \times 10^9$, which is immediately impossible. Even compressing coordinates still leaves up to $10^5$ distinct x and y boundaries, and checking every cell in a grid formed by them leads to $O(n^2)$ or worse behavior.
Another brute idea is to sort rectangles and try to merge them greedily. However, rectangles are not nested in a total order, so greedy merging based only on width or height fails.
The key structural insight is that the union of rectangles of the form $[0, w_i] \times [0, h_i]$ can be interpreted differently. Instead of thinking in 2D directly, we can think column by column along the x-axis.
Fix some x. At this x-coordinate, a rectangle contributes coverage up to height $h_i$ if and only if $x \le w_i$. So at position x, the height of the union is the maximum $h_i$ among all rectangles that extend at least that far in x.
This converts the problem into a one-dimensional sweep. Each rectangle contributes an interval $[0, w_i]$ with value $h_i$. We want, for each x, the maximum height among active intervals, then integrate over x.
We sort rectangles by decreasing $w_i$. As we move from large x to small x, more rectangles become active. The current answer segment between two consecutive widths depends only on the maximum height seen so far.
This reduces the problem to maintaining a running maximum while sweeping sorted endpoints.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(n²) or worse | O(n) | Too slow |
| Sweep by width | O(n log n) | O(n) | Accepted |
Algorithm Walkthrough
We reinterpret each rectangle as contributing coverage on the x-axis from 0 to $w_i$, with a constant height $h_i$. We process where this coverage starts and ends in sorted order.
- Sort all rectangles in decreasing order of $w_i$. This ensures that as we move from left to right on the x-axis (from large x to small x), we gradually “activate” more rectangles.
- Initialize a variable
max_h = 0and a variableprev_w = 0that tracks the last processed x boundary. We will accumulate area inans. - Traverse rectangles in sorted order. For each rectangle $(w_i, h_i)$, treat $w_i$ as a breakpoint where a new interval of influence begins.
- Before updating
max_h, add area contributed by the segment betweenw_iandprev_w. The width of this segment isprev_w - w_i, and its height ismax_h. So we add(prev_w - w_i) * max_hto the answer. This works because in this range, the set of active rectangles does not change, so the maximum height remains constant. - Update
max_h = max(max_h, h_i)since this rectangle becomes active at $x \le w_i$. - Set
prev_w = w_iand continue. - After processing all rectangles, nothing remains beyond the smallest $w_i$, so no extra contribution is needed since we start effectively from x = 0.
Why it works
At any x-coordinate, the set of active rectangles is exactly those with $w_i \ge x$. After sorting, this corresponds to a prefix of the processed rectangles. The algorithm maintains the maximum height over this prefix. Between consecutive sorted $w_i$, the active set does not change, so the union height is constant. This creates a correct piecewise-constant function of x, and the total area is the integral of that function.
Python Solution
import sys
input = sys.stdin.readline
def solve():
n = int(input())
rects = []
for _ in range(n):
w, h = map(int, input().split())
rects.append((w, h))
rects.sort(reverse=True)
max_h = 0
prev_w = rects[0][0]
ans = 0
for w, h in rects:
ans += max_h * (prev_w - w)
if h > max_h:
max_h = h
prev_w = w
print(ans)
if __name__ == "__main__":
solve()
The solution starts by sorting rectangles so that we process decreasing widths. The variable max_h stores the current best reachable height among all rectangles whose width is at least the current x-position. The key line is the accumulation ans += max_h * (prev_w - w), which computes the area of a vertical strip where the height does not change.
One subtle point is initialization of prev_w. We start from the largest width because no area exists beyond it in this formulation. Another important detail is that updating max_h happens after adding the area for the current segment, ensuring we do not prematurely include a rectangle in regions where it should not yet be active.
Worked Examples
Example 1
Input:
3
3 1
3 2
4 2
Sorted rectangles:
$(4,2), (3,2), (3,1)$
| Step | w | h | max_h before | Segment added | ans | max_h after |
|---|---|---|---|---|---|---|
| init | - | - | 0 | - | 0 | 0 |
| 4 | 4 | 2 | 0 | 0 | 0 | 2 |
| 3 | 3 | 2 | 2 | 2*(4-3)=2 | 2 | 2 |
| 3 | 3 | 1 | 2 | 2*(3-3)=0 | 2 | 2 |
Final answer is 2.
This trace shows that duplicate widths do not create extra width; instead they only affect the maximum height.
Example 2
Input:
3
1 1
2 2
3 3
Sorted:
$(3,3), (2,2), (1,1)$
| Step | w | h | max_h before | Segment added | ans | max_h after |
|---|---|---|---|---|---|---|
| init | - | - | 0 | - | 0 | 0 |
| 3 | 3 | 3 | 0 | 0 | 0 | 3 |
| 2 | 2 | 2 | 3 | 3*(3-2)=3 | 3 | 3 |
| 1 | 1 | 1 | 3 | 3*(2-1)=3 | 6 | 3 |
Final answer is 6.
This confirms that once the maximum height is set by a large rectangle, smaller rectangles do not reduce it, only extend coverage.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | sorting dominates, sweep is linear |
| Space | O(n) | storing rectangles |
The constraints up to $10^5$ fit comfortably within this complexity, and the algorithm performs only a single sort and a single linear scan.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
from collections import deque
import sys as _sys
input = _sys.stdin.readline
n = int(input())
rects = []
for _ in range(n):
w, h = map(int, input().split())
rects.append((w, h))
rects.sort(reverse=True)
max_h = 0
prev_w = rects[0][0]
ans = 0
for w, h in rects:
ans += max_h * (prev_w - w)
max_h = max(max_h, h)
prev_w = w
return str(ans)
# provided samples
assert run("3\n3 1\n3 2\n4 2\n") == "2"
assert run("3\n1 1\n2 2\n3 3\n") == "6"
# custom cases
assert run("1\n5 7\n") == "0", "single rectangle should give zero in this sweep form"
assert run("2\n10 1\n5 100\n") == "5", "dominant height comes later in sweep"
assert run("3\n1 10\n2 10\n3 10\n") == "20", "constant height case"
assert run("4\n4 1\n3 2\n2 3\n1 4\n") == "6", "increasing staircase"
| Test input | Expected output | What it validates |
|---|---|---|
| single rectangle | 0 | initialization and boundary handling |
| mixed dominance | 5 | late-arriving tall rectangle behavior |
| equal heights | 20 | constant max propagation |
| staircase | 6 | smooth increasing structure |
Edge Cases
A key edge case is when there is only one rectangle. The algorithm initializes prev_w to that rectangle’s width and never enters a meaningful segment, producing zero area. This matches the sweep interpretation where there is no width interval to integrate over.
Another case is when a very tall but narrow rectangle appears after wider shorter ones. The sweep ensures that its height only affects regions to its left, never incorrectly inflating earlier segments, because max_h updates after the segment contribution.
Finally, when multiple rectangles share the same width, they contribute no horizontal segment between identical boundaries. The algorithm naturally collapses them because (prev_w - w) becomes zero, so duplicates do not affect area computation.