📖 Chapter 2.5: Casework and Rectangle Geometry
⏱ Estimated reading time: 55 minutes | Difficulty: 🟢 Beginner (Core USACO Bronze Skill)
Prerequisites
- Basic C++ syntax (Chapters 2.1–2.2)
if/elseconditional statements- Reading integers from input and printing output
- Understanding that
xrepresents the horizontal coordinate andyrepresents the vertical coordinate in a Cartesian plane
🎯 Learning Goals
After finishing this chapter, you will be able to:
- Recognize problems that require casework and systematically list all cases
- Use the method “draw first, list boundaries next, then write code” to avoid missing cases
- Handle rectangle intersection, coverage, and area problems on the coordinate plane
- Determine whether two rectangles intersect and compute their intersection area
- Use the difference array idea to count rectangle coverage on a grid
- Understand and implement common USACO Bronze rectangle coverage problems
Learning Path for This Chapter
This chapter has two main themes:
- Casework: When a problem has multiple possible situations, how do we handle them without missing or double-counting cases?
- Rectangle geometry: When a problem involves coordinates, rectangles, coverage, or intersection, how do we convert the geometry into
min/maxand area calculations?
For beginners, the recommended learning order is:
Draw a picture to understand the problem
↓
Find key boundaries: equal, just touching, fully covering, fully separate
↓
Express those situations with if/else or min/max
↓
Test your code using samples and boundary cases you create yourself
💡 The focus of this chapter is not memorizing a single template, but learning how to break “complicated-looking situations” into clear, verifiable smaller cases.
2.5.1 Casework
What Is Casework?
When the answer to a problem depends on several “mutually exclusive situations,” we need to list these situations one by one and handle each separately.
Here are some simple examples:
- Is a number positive, negative, or zero?
- Are two intervals disjoint, just touching, or overlapping?
- Is a point inside a rectangle, on its boundary, or outside it?
- Is a rectangle partially covered by another rectangle?
These problems have one thing in common: you usually cannot finish with just one formula. You first need to determine which situation the current input belongs to.
Core principles:
- Completeness: Do not miss any possible situation.
- Mutual exclusiveness: Different situations should preferably not overlap; if they do overlap, make the priority clear.
- Boundary checking: Pay special attention to cases such as
equal,just touching, andjust fully covering.
General Steps for Casework
When solving a casework problem, follow these four steps:
- Draw a picture: Draw the objects in the problem, such as intervals, rectangles, or movement directions.
- Find boundaries: Think about when the answer changes, such as when two interval endpoints become equal.
- List cases: List all possible cases and try to make sure none are missing.
- Write conditions: Translate each case into C++
if / else if / elsestatements.
For example:
if (first case) {
// Handle the first case
} else if (second case) {
// Handle the second case
} else {
// Handle the remaining case
}
The final else is important. It often acts as a fallback: if none of the earlier explicitly listed cases are true, the program enters the final case.
Example: Classifying Two 1D Intervals
Problem statement:
Given two closed intervals [a, b] and [c, d], where a <= b and c <= d, determine their relationship:
- Completely disjoint: There is a gap between the two intervals.
- Touching at exactly one endpoint: The two intervals share one endpoint but do not overlap with positive length.
- One contains the other: One interval completely covers the other interval.
- Partially overlapping: The two intervals have a common part, but neither completely contains the other.
Before writing code, draw the cases first:
Case 1: Completely disjoint
[a-----b] [c-----d]
or
[c-----d] [a-----b]
Case 2: Touching at exactly one endpoint
[a-----b]
[c-----d] where b == c
Case 3: One interval contains the other
[a-----------b]
[c-----d]
Case 4: Partially overlapping
[a--------b]
[c--------d]
Now translate the picture into conditions.
Key Boundary: The Difference Between < and ==
- If
b < c, then[a,b]is to the left of[c,d], with a gap between them. - If
b == c, then the two intervals only touch at one point. - If
b > c, then they overlap or one contains the other along the horizontal axis.
Therefore, when doing casework, we often handle more special cases first, such as “completely disjoint” and “touching at an endpoint.”
💡 C++ Code (31 lines)
#include <bits/stdc++.h>
using namespace std;
int main() {
long long a, b, c, d;
cin >> a >> b >> c >> d; // Intervals [a,b] and [c,d]
// If the input does not guarantee left endpoint <= right endpoint, fix it first
if (a > b) swap(a, b);
if (c > d) swap(c, d);
if (b < c || d < a) {
cout << "Completely disjoint\n";
} else if (b == c || d == a) {
cout << "Touching at exactly one endpoint\n";
} else if (a <= c && d <= b) {
cout << "[c,d] is inside [a,b] (or equal)\n";
} else if (c <= a && b <= d) {
cout << "[a,b] is inside [c,d] (or equal)\n";
} else {
cout << "Partially overlapping\n";
}
return 0;
}
Beginner Question: Why Does the Order Matter?
In an if / else if chain, the program checks conditions from top to bottom. Once it finds the first true condition, it executes that block and skips the rest.
For example, the two intervals [1, 5] and [1, 5]:
- They satisfy
[c,d] is inside [a,b]. - They also satisfy
[a,b] is inside [c,d].
This is not necessarily wrong; it simply means the two intervals are equal. We only need to decide which output is appropriate for the classification required by the problem.
💡 If the problem asks you to handle “exactly equal” as a separate case, then write
if (a == c && b == d)before the containment checks.
Casework Techniques
Technique 1: Sort or normalize input to reduce the number of cases
Sorting or normalizing input can merge symmetric cases:
// Example: ensure a <= b, so we do not need to separately handle a<b and a>b
if (a > b) swap(a, b);
Technique 2: Use min/max to simplify interval operations
If you only care about the overlap length of two intervals, you may not need to list all relationships. You can compute the intersection directly.
// Overlap length of intervals [a,b] and [c,d]
long long overlap = max(0LL, min(b, d) - max(a, c));
Meaning of this formula:
- The left endpoint of the intersection is the larger of the two left endpoints:
max(a, c). - The right endpoint of the intersection is the smaller of the two right endpoints:
min(b, d). - If the right endpoint is less than or equal to the left endpoint, there is no positive-length overlap.
Note: The formula above is suitable when treating intervals as
[a,b)and[c,d), a “left-closed, right-open” length model. If you are working with closed intervals and counting integer points, the formula changes slightly.
Technique 3: Draw pictures and list boundary cases
For casework problems, draw the picture and verify all boundary cases one by one. At minimum, check:
- Completely separate
- Just touching
- Partially overlapping
- One completely containing the other
- Exactly equal
Technique 4: Prefer checking the easier opposite condition
Sometimes directly determining “intersecting” is complicated, but determining “not intersecting” is simple.
For example, two intervals are disjoint in only two ways:
b <= c || d <= a
So intersecting is the opposite:
!(b <= c || d <= a)
We will use the same idea for rectangle intersection.
USACO Bronze Typical: Direction Simulation
Problem statement (USACO style):
A cow stands on a 2D plane. Its initial position is (0, 0), and it initially faces north. Then N instructions are given. Each instruction is one of the following:
left: Turn left by 90 degrees in place.right: Turn right by 90 degrees in place.forward D: Move forwardDsteps in the current direction.
Output the cow’s final coordinates after all instructions are executed.
This looks like a simulation problem, but it also involves casework:
- Is the current direction north, east, south, or west?
- Is the current instruction a left turn, right turn, or forward movement?
- If moving forward, different directions change
xandydifferently.
Naive Approach: Direct Casework
The most intuitive implementation is:
if (dir == "N") y += d;
else if (dir == "S") y -= d;
else if (dir == "E") x += d;
else if (dir == "W") x -= d;
This is easy to understand, but if turning logic is also implemented with string-based casework, the code becomes long.
Cleaner Approach: Direction Indices
We can number the four directions clockwise:
0: North
1: East
2: South
3: West
Then turning right means adding 1, and turning left means subtracting 1. To avoid negative values, we can write:
// Turn left: dir - 1
// Add 4 to avoid a negative number, then take modulo 4
(dir + 3) % 4
💡 C++ Code (28 lines)
#include <bits/stdc++.h>
using namespace std;
int main() {
// Direction encoding: 0=N, 1=E, 2=S, 3=W
// dx/dy store the movement for each direction
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
int x = 0, y = 0, dir = 0; // Initial position and direction
int n;
cin >> n;
while (n--) {
string cmd;
cin >> cmd;
if (cmd == "left") {
dir = (dir + 3) % 4; // Turn left = (dir - 1 + 4) % 4
} else if (cmd == "right") {
dir = (dir + 1) % 4; // Turn right
} else {
int d;
cin >> d;
x += dx[dir] * d;
y += dy[dir] * d;
}
}
cout << x << " " << y << "\n";
return 0;
}
2.5.2 Rectangle Geometry
Representing Rectangles in a Coordinate System
In programming contests, rectangles are usually axis-aligned rectangles, meaning their sides are parallel to the coordinate axes. A rectangle is commonly represented by two opposite corners:
- Lower-left corner:
(x1, y1) - Upper-right corner:
(x2, y2)
Usually:
x1 < x2, y1 < y2
Drawn on the coordinate plane:
y
↑
y2 +----------+
| |
y1 +----------+→ x
x1 x2
If the problem does not guarantee x1 < x2 and y1 < y2, normalize after reading:
if (x1 > x2) swap(x1, x2);
if (y1 > y2) swap(y1, y2);
Why Is the Area (x2 - x1) * (y2 - y1)?
The width of the rectangle is the difference in x coordinates:
width = x2 - x1
The height of the rectangle is the difference in y coordinates:
height = y2 - y1
So the area is:
area = (x2 - x1) * (y2 - y1)
For example, for the rectangle from (1, 2) to (4, 6):
- Width:
4 - 1 = 3 - Height:
6 - 2 = 4 - Area:
3 * 4 = 12
Important Convention: Treat Rectangles as Half-Open Intervals
In many programming contest problems, a rectangle (x1, y1, x2, y2) represents:
x1 <= x < x2
y1 <= y < y2
That is, the left and bottom boundaries are included, while the right and top boundaries are excluded. This convention is useful because:
- The area is exactly
(x2 - x1) * (y2 - y1). - If two rectangles only touch at an edge, their intersection area is
0. - It makes difference arrays for grid coverage easier to use.
For example, the rectangle (0,0)-(2,2) covers the following four 1×1 cells:
(0,1) (1,1)
(0,0) (1,0)
Its area is 2 * 2 = 4.
Determining Whether Two Rectangles Intersect
Key rule: Two rectangles do not intersect if and only if one rectangle is completely to the left, right, below, or above the other.
In other words, if any of the following is true, the two rectangles have no positive-area intersection:
- A is to the left of B:
ax2 <= bx1 - B is to the left of A:
bx2 <= ax1 - A is below B:
ay2 <= by1 - B is below A:
by2 <= ay1
One case in picture form:
A is to the left of B:
A B
+---+ +---+
| | | |
+---+ +---+
ax2 <= bx1
Therefore, to determine intersection, we can first check “not intersecting” and then take the opposite.
// Rectangle A: (ax1,ay1)-(ax2,ay2)
// Rectangle B: (bx1,by1)-(bx2,by2)
bool intersects(long long ax1, long long ay1, long long ax2, long long ay2,
long long bx1, long long by1, long long bx2, long long by2) {
// A is completely left/right/below/above B
if (ax2 <= bx1 || bx2 <= ax1 || ay2 <= by1 || by2 <= ay1)
return false;
return true;
}
💡 We use
<=here because if two rectangles only touch edge-to-edge, they do not have positive-area intersection.
Intersection Area
To compute the intersection of two rectangles, split the problem into two one-dimensional problems:
- Overlap length in the
xdirection - Overlap length in the
ydirection
The lower-left and upper-right corners of the intersection rectangle are:
Intersection lower-left: max(two left boundaries), max(two bottom boundaries)
Intersection upper-right: min(two right boundaries), min(two top boundaries)
In code:
long long intersection_area(long long ax1, long long ay1, long long ax2, long long ay2,
long long bx1, long long by1, long long bx2, long long by2) {
long long ix1 = max(ax1, bx1);
long long iy1 = max(ay1, by1);
long long ix2 = min(ax2, bx2);
long long iy2 = min(ay2, by2);
if (ix2 <= ix1 || iy2 <= iy1) return 0; // No intersection, or only touching at boundary
return (ix2 - ix1) * (iy2 - iy1);
}
Step-by-step example:
Rectangle A: (1,1)-(4,4)
Rectangle B: (2,2)-(6,5)
Step 1: Find the lower-left corner of the intersection
x = max(1,2) = 2
y = max(1,2) = 2
So the lower-left corner is (2,2)
Step 2: Find the upper-right corner of the intersection
x = min(4,6) = 4
y = min(4,5) = 4
So the upper-right corner is (4,4)
Step 3: Compute the area
width = 4 - 2 = 2
height = 4 - 2 = 2
area = 2 * 2 = 4
Now consider an example with no positive-area overlap:
Rectangle A: (0,0)-(2,2)
Rectangle B: (2,0)-(4,2)
They only touch at an edge.
Intersection width = min(2,4) - max(0,2) = 2 - 2 = 0
So the intersection area is 0.
Union Area of Two Rectangles
The union area of two rectangles means the area covered by at least one of them.
If we write:
Union area = area of A + area of B
there is a problem: the overlapping part is counted twice.
So we subtract the overlapping part once:
Union area = area of A + area of B - intersection area of A and B
This is the simplest form of the inclusion-exclusion principle.
long long union_area(long long ax1, long long ay1, long long ax2, long long ay2,
long long bx1, long long by1, long long bx2, long long by2) {
long long areaA = (ax2 - ax1) * (ay2 - ay1);
long long areaB = (bx2 - bx1) * (by2 - by1);
long long inter = intersection_area(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2);
return areaA + areaB - inter;
}
Example:
Area of rectangle A = 12
Area of rectangle B = 10
Overlapping area = 4
If directly added: 12 + 10 = 22, but the overlap was counted twice.
Correct answer: 12 + 10 - 4 = 18
💡 This formula is very useful for the union area of two rectangles. But for three or more rectangles, overlaps become more complicated, and we usually need sweep line, difference arrays, or a more systematic inclusion-exclusion method.
Point-in-Rectangle Test
When determining the relationship between a point and a rectangle, first check what the problem asks for:
- If the problem asks whether the point is inside the rectangle “including the boundary,” use
<=. - If the problem asks whether the point is strictly inside, use
<. - If the problem asks you to classify the boundary separately, check “boundary” and “inside” separately.
bool point_in_rect(long long px, long long py,
long long x1, long long y1, long long x2, long long y2) {
return x1 <= px && px <= x2 && y1 <= py && py <= y2;
}
For example, for rectangle (0,0)-(3,2):
- Point
(1,1)is inside. - Point
(0,1)is on the left boundary. - Point
(3,2)is on the upper-right corner. - Point
(4,1)is outside.
Advanced: Coverage Area of N Rectangles (Difference Array Method)
When many rectangles overlap, marking every grid cell for every rectangle may be too slow. If the coordinate range is small, we can use a 2D difference array to count how many times each cell is covered.
First Understand 1D Difference Arrays
Suppose we have an array and want to add 1 to every position in interval [l, r). We can write:
diff[l]++;
diff[r]--;
After computing prefix sums from left to right, we know how many times each position was added.
A 2D difference array uses the same idea, but it changes a “line segment” into a “rectangle.”
How Does a 2D Difference Array Add 1 to a Rectangle?
To add 1 to the covered area of rectangle [x1, x2) × [y1, y2), update four corners:
diff[y1][x1]++;
diff[y1][x2]--;
diff[y2][x1]--;
diff[y2][x2]++;
You can understand it as:
- Start adding the effect at the lower-left corner.
- Cancel the effect after the right boundary.
- Cancel the effect after the top boundary.
- The upper-right corner was canceled twice, so add it back once.
💡 C++ Code (45 lines)
#include <bits/stdc++.h>
using namespace std;
const int MAX_COORD = 1000; // Assume coordinates are in the range 0~1000
int diff[MAX_COORD + 2][MAX_COORD + 2]; // Allocate extra space for x2/y2 updates
// Add 1 to the rectangle [x1,x2) × [y1,y2)
void add_rect(int x1, int y1, int x2, int y2) {
diff[y1][x1]++;
diff[y1][x2]--;
diff[y2][x1]--;
diff[y2][x2]++;
}
int main() {
int n;
cin >> n;
while (n--) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
add_rect(x1, y1, x2, y2);
}
// Restore the coverage count of each cell using 2D prefix sums
for (int y = 0; y <= MAX_COORD; y++) {
for (int x = 0; x <= MAX_COORD; x++) {
if (y > 0) diff[y][x] += diff[y - 1][x];
if (x > 0) diff[y][x] += diff[y][x - 1];
if (y > 0 && x > 0) diff[y][x] -= diff[y - 1][x - 1];
}
}
// Count 1×1 cells covered by at least one rectangle
// The largest lower-left corner is (999,999), because [999,1000) is the last cell
long long covered = 0;
for (int y = 0; y < MAX_COORD; y++) {
for (int x = 0; x < MAX_COORD; x++) {
if (diff[y][x] > 0) covered++;
}
}
cout << covered << "\n";
return 0;
}
Why Are We Counting Cells, Not Points?
When the coordinate range is 0~1000, the cells are usually:
Horizontal intervals: [0,1), [1,2), ..., [999,1000)
Vertical intervals: [0,1), [1,2), ..., [999,1000)
So there are 1000 × 1000 cells of size 1×1. The array element diff[y][x] can be understood as the coverage count of the small cell whose lower-left corner is (x,y).
⚠️ Common Mistakes
| Mistake | Cause | Fix |
|---|---|---|
| Missing boundary cases | Only considering the “general case” and ignoring equal endpoints | List all cases and verify <= vs < |
| Treating touching as intersection | Two rectangles only touch at an edge, so the intersection area is actually 0 | Use <=, not <, when checking non-intersection |
| Integer overflow | Coordinates may be large, so x * y may exceed int | Use long long for areas and final answers |
| Assuming rectangle corner order incorrectly | The input may not guarantee x1 < x2 and y1 < y2 | Normalize with swap when necessary |
| Difference array out of bounds | Coordinates exceed array limits, or no extra space is allocated for x2/y2 | Usually allocate one extra row/column, such as 1002 × 1002 |
| Confusing points with cells | The number of coordinate points is not the same as the number of 1×1 cells | Area coverage problems count cells, not coordinate points |
| Mixing up row and column indices | Using diff[y][x] and diff[x][y] inconsistently | Choose one convention, such as first dimension = y, second dimension = x, and keep it consistent |
Debugging Suggestions
After writing a rectangle solution, create these test cases yourself:
- Completely disjoint: The answer should be
0. - Just touching at an edge: The intersection area should also be
0. - One rectangle completely contains the other: The intersection area should equal the smaller rectangle’s area.
- Two rectangles are exactly the same: The intersection area should equal either rectangle’s area.
- Only partially overlapping: Manually compute the width and height once, then compare with your program’s output.
Chapter Summary
The most important ideas in this chapter can be summarized in three sentences:
- Draw before doing casework: Do not write conditions by intuition; draw all cases first.
- Split rectangle problems into two directions: Handle the
xdirection andydirection separately, then combine the results. - Think of difference arrays for coverage problems: When there are many rectangles and the coordinate range is small, a 2D difference array is clearer than brute-force marking per rectangle.
Common formulas:
// Rectangle area
area = (x2 - x1) * (y2 - y1);
// Intersection boundaries of two rectangles
ix1 = max(ax1, bx1);
iy1 = max(ay1, by1);
ix2 = min(ax2, bx2);
iy2 = min(ay2, by2);
// Intersection area of two rectangles
intersection = max(0LL, ix2 - ix1) * max(0LL, iy2 - iy1);
💪 Exercises
🟢 Problem 1: Rectangle Intersection Area
Problem statement:
Given two axis-aligned rectangles A and B. Each rectangle is represented by four integers:
x1 y1 x2 y2
where (x1, y1) is the lower-left corner and (x2, y2) is the upper-right corner. It is guaranteed that x1 < x2 and y1 < y2.
Determine whether the two rectangles have a positive-area intersection:
- If they do, output the intersection area.
- If they do not, output
0.
Note: If two rectangles only touch at the boundary, such as when the right boundary of one rectangle equals the left boundary of another, the intersection area is 0.
Input format:
ax1 ay1 ax2 ay2
bx1 by1 bx2 by2
Output format:
One integer, the intersection area of the two rectangles
Sample input:
1 1 4 4
2 2 6 5
Sample output:
4
Sample explanation:
The intersection of the two rectangles is (2,2)-(4,4), with width 2, height 2, and area 4.
✅ Full Solution
#include <bits/stdc++.h>
using namespace std;
int main() {
long long ax1, ay1, ax2, ay2;
long long bx1, by1, bx2, by2;
cin >> ax1 >> ay1 >> ax2 >> ay2;
cin >> bx1 >> by1 >> bx2 >> by2;
long long ix1 = max(ax1, bx1);
long long iy1 = max(ay1, by1);
long long ix2 = min(ax2, bx2);
long long iy2 = min(ay2, by2);
if (ix2 <= ix1 || iy2 <= iy1) {
cout << 0 << "\n";
} else {
cout << (ix2 - ix1) * (iy2 - iy1) << "\n";
}
return 0;
}
🟡 Problem 2: Cow Grazing Area (USACO Bronze Style)
Problem statement:
Farmer John has N rectangular pastures. Each pasture is axis-aligned, and all coordinates are integers between 0 and 1000.
Each pasture is represented by its lower-left corner (x1, y1) and upper-right corner (x2, y2). It covers all 1×1 cells satisfying:
x1 <= x < x2
y1 <= y < y2
If multiple pastures overlap, the overlapping part is counted only once. Find the total number of cells covered by at least one pasture, which is also the total covered area.
Input format:
N
x1 y1 x2 y2
x1 y1 x2 y2
...
Output format:
One integer, the number of covered 1×1 cells
Sample input:
3
0 0 2 2
1 1 3 3
4 4 5 5
Sample output:
8
Sample explanation:
- The first pasture has area
4. - The second pasture has area
4. - They overlap by
1cell. - The third pasture has area
1. - The total covered area is
4 + 4 - 1 + 1 = 8.
✅ Full Solution
Idea: Use a difference array to mark how many times each 1×1 cell is covered, then count cells whose coverage is >= 1.
#include <bits/stdc++.h>
using namespace std;
int diff[1002][1002];
int main() {
int n;
cin >> n;
while (n--) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
// Add 1 to the rectangle [x1,x2) × [y1,y2)
diff[y1][x1]++;
diff[y1][x2]--;
diff[y2][x1]--;
diff[y2][x2]++;
}
long long ans = 0;
for (int y = 0; y <= 1000; y++) {
for (int x = 0; x <= 1000; x++) {
if (y > 0) diff[y][x] += diff[y - 1][x];
if (x > 0) diff[y][x] += diff[y][x - 1];
if (y > 0 && x > 0) diff[y][x] -= diff[y - 1][x - 1];
// Only count 1×1 cells whose lower-left corner is (x,y), up to (999,999)
if (x < 1000 && y < 1000 && diff[y][x] > 0) ans++;
}
}
cout << ans << "\n";
return 0;
}
🔴 Problem 3: Rectangle Classification Problem (Comprehensive)
Problem statement:
Given an axis-aligned rectangle and N points, determine the relationship between each point and the rectangle:
- If the point is on one of the four sides of the rectangle, output
Boundary. - If the point is inside the rectangle but not on the boundary, output
Inside. - If the point is neither inside nor on the boundary, output
Outside.
The rectangle is given by its lower-left corner (x1, y1) and upper-right corner (x2, y2). It is guaranteed that x1 < x2 and y1 < y2.
Input format:
x1 y1 x2 y2
N
px py
px py
...
Output format:
For each point, output one line: Boundary, Inside, or Outside.
Sample input:
0 0 4 3
5
1 1
0 2
4 3
5 1
2 3
Sample output:
Inside
Boundary
Boundary
Outside
Boundary
Sample explanation:
(1,1)has both coordinates strictly inside the rectangle range, so it is inside.(0,2)is on the left boundary.(4,3)is the upper-right corner, so it is on the boundary.(5,1)is outside the rectangle.(2,3)is on the top boundary.
✅ Full Solution
#include <bits/stdc++.h>
using namespace std;
int main() {
long long x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
int n;
cin >> n;
while (n--) {
long long px, py;
cin >> px >> py;
bool on_vertical_edge = (px == x1 || px == x2) && (y1 <= py && py <= y2);
bool on_horizontal_edge = (py == y1 || py == y2) && (x1 <= px && px <= x2);
bool on_edge = on_vertical_edge || on_horizontal_edge;
bool inside = x1 < px && px < x2 && y1 < py && py < y2;
if (on_edge) cout << "Boundary\n";
else if (inside) cout << "Inside\n";
else cout << "Outside\n";
}
return 0;
}
🔴 Problem 4: USACO Bronze — Blocked Billboard
Problem statement:
Two billboards are placed on a wall. Later, a truck parks in front of the wall and blocks part of the visible area. The projections of the billboards and the truck on the wall can all be treated as axis-aligned rectangles.
You are given:
- The rectangle coordinates of the first billboard.
- The rectangle coordinates of the second billboard.
- The rectangle coordinates of the truck’s blocking region.
Compute the total visible area of the two billboards.
Notes:
- The two billboards do not block each other.
- The truck may not block a billboard at all.
- The truck may block only part of a billboard.
- The truck may completely block a billboard.
- If the truck only touches the boundary of a billboard, the visible area does not decrease.
Input format:
Three lines, each containing 4 integers:
x1 y1 x2 y2
x1 y1 x2 y2
x1 y1 x2 y2
They represent billboard 1, billboard 2, and the truck’s blocking region respectively. Each rectangle is represented by its lower-left and upper-right corners.
Output format:
One integer, the total visible area of the two billboards
Sample input:
1 2 3 5
6 0 10 4
2 1 8 3
Sample output:
17
Sample explanation:
Billboard 1 has area (3 - 1) * (5 - 2) = 6. The intersection of the truck and billboard 1 is (2,2)-(3,3), with area 1, so the visible area of billboard 1 is 5.
Billboard 2 has area (10 - 6) * (4 - 0) = 16. The intersection of the truck and billboard 2 is (6,1)-(8,3), with area 4, so the visible area of billboard 2 is 12.
The total visible area is 5 + 12 = 17.
✅ Full Solution
Core idea: Casework + inclusion-exclusion. Total area = billboard 1 area + billboard 2 area - area of billboard 1 blocked by the truck - area of billboard 2 blocked by the truck. Each blocked area is computed with the rectangle intersection formula.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// Compute the intersection area of two rectangles
ll inter_area(ll ax1, ll ay1, ll ax2, ll ay2,
ll bx1, ll by1, ll bx2, ll by2) {
ll ix1 = max(ax1, bx1), iy1 = max(ay1, by1);
ll ix2 = min(ax2, bx2), iy2 = min(ay2, by2);
if (ix2 <= ix1 || iy2 <= iy1) return 0;
return (ix2 - ix1) * (iy2 - iy1);
}
int main() {
ll a1x1, a1y1, a1x2, a1y2; // Billboard 1
ll a2x1, a2y1, a2x2, a2y2; // Billboard 2
ll tx1, ty1, tx2, ty2; // Truck
cin >> a1x1 >> a1y1 >> a1x2 >> a1y2;
cin >> a2x1 >> a2y1 >> a2x2 >> a2y2;
cin >> tx1 >> ty1 >> tx2 >> ty2;
ll area1 = (a1x2 - a1x1) * (a1y2 - a1y1);
ll area2 = (a2x2 - a2x1) * (a2y2 - a2y1);
ll covered1 = inter_area(a1x1, a1y1, a1x2, a1y2, tx1, ty1, tx2, ty2);
ll covered2 = inter_area(a2x1, a2y1, a2x2, a2y2, tx1, ty1, tx2, ty2);
cout << area1 + area2 - covered1 - covered2 << "\n";
return 0;
}
Complexity analysis: Time O(1), space O(1). We only compute a few rectangle areas and intersections, so the solution runs in constant time.
💡 Chapter connection: Rectangle geometry is one of the high-frequency USACO Bronze topics (about 15%) and often combines with prefix sums or difference arrays. Once mastered, it can be directly applied to problems such as “coverage area” and “overlap detection.”