USACO 2018 January · Bronze · all three problems
Problem 1 — Blocked Billboard II
Statement
A rectangular billboard is axis-aligned with corners (x1,y1) and (x2,y2). A rectangular truck (also axis-aligned) parks in front of part of it. The truck is guaranteed to cover the billboard along a full side — i.e. the unblocked portion of the billboard, if any, is itself a single axis-aligned rectangle (possibly empty, possibly the entire billboard). Output the area of the visible part of the billboard.
Constraints
- All coordinates are integers in [−1000, 1000].
- The truck either fully covers the billboard, doesn't intersect it at all, or covers it along one full edge.
- Time limit: 2s.
Key idea
Intersect the truck with the billboard to get the overlap rectangle (clip on each axis). Because the truck covers a full side of the billboard, the visible portion is itself a rectangle. Concretely: if the overlap spans the full billboard width, shrink vertically; if it spans the full billboard height, shrink horizontally; otherwise (no full-side coverage / no overlap) the visible area is the original billboard area minus the overlap area.
Complexity target
O(1).
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
// billboard: (bx1, by1) - (bx2, by2)
// truck: (tx1, ty1) - (tx2, ty2)
int bx1, by1, bx2, by2, tx1, ty1, tx2, ty2;
cin >> bx1 >> by1 >> bx2 >> by2;
cin >> tx1 >> ty1 >> tx2 >> ty2;
// Clip truck to billboard.
int ox1 = max(bx1, tx1), oy1 = max(by1, ty1);
int ox2 = min(bx2, tx2), oy2 = min(by2, ty2);
long long billboard = (long long)(bx2 - bx1) * (by2 - by1);
if (ox1 >= ox2 || oy1 >= oy2) {
// No overlap at all.
cout << billboard << "\n";
return 0;
}
bool fullX = (ox1 == bx1 && ox2 == bx2); // overlap spans billboard width
bool fullY = (oy1 == by1 && oy2 == by2); // overlap spans billboard height
if (fullX && fullY) { cout << 0 << "\n"; return 0; }
// Guaranteed: at least one of fullX, fullY holds, so visible region is a rectangle.
long long covered = (long long)(ox2 - ox1) * (oy2 - oy1);
cout << billboard - covered << "\n";
}
Pitfalls
- Half-open vs closed rectangles. USACO uses the (x1,y1)–(x2,y2) "lower-left / upper-right" convention; area is (x2−x1)(y2−y1), no +1.
- Edge touching is not overlap. Use strict
>=on the clipped extents to detect "no overlap". - Don't forget the "no overlap" branch. The truck need not touch the billboard at all.
- This is the easier Billboard II — the trick of a guaranteed full-side cover makes the answer a single rectangle subtraction.
Problem 2 — Lifeguards
Statement
N lifeguards each cover a half-open time interval [ai, bi). Farmer John must fire exactly one lifeguard. Output the maximum total amount of time during which at least one of the remaining N−1 lifeguards is on duty.
Constraints
- 1 ≤ N ≤ 100.
- 0 ≤ ai < bi ≤ 109.
- Time limit: 2s.
Key idea
N is tiny. For each candidate to fire, compute the union length of the other N−1 intervals by sorting + sweeping, and take the max. The "remove one interval" trick is just brute force at N ≤ 100: O(N²) intervals, O(N log N) per union — well under a second.
Complexity target
O(N² log N), with N ≤ 100 that's ~10⁶ ops.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int N;
vector<pair<int,int>> iv;
ll unionLen(int skip) {
vector<pair<int,int>> v;
v.reserve(N);
for (int i = 0; i < N; ++i) if (i != skip) v.push_back(iv[i]);
sort(v.begin(), v.end());
ll total = 0;
int curL = -1, curR = -1;
for (auto& p : v) {
if (p.first > curR) {
if (curR > curL) total += curR - curL;
curL = p.first; curR = p.second;
} else {
curR = max(curR, p.second);
}
}
if (curR > curL) total += curR - curL;
return total;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N;
iv.resize(N);
for (auto& p : iv) cin >> p.first >> p.second;
ll best = 0;
for (int i = 0; i < N; ++i) best = max(best, unionLen(i));
cout << best << "\n";
}
Pitfalls
- Half-open intervals. Two intervals [1,3) and [3,5) are adjacent, not overlapping; union length is 4. The merge condition is
p.first > curR, not>=. - Must fire exactly one — even if the union is already maximal, you can't keep everyone.
- Coordinates up to 10⁹ — use
long longfor the union length sum (though a single interval fits in int). - Silver / Platinum variants exist with fire-K-lifeguards (cpids 786 and 792); don't bring their DP machinery to Bronze.
Problem 3 — Out of Place
Statement
The cows enter the barn in some order with distinct heights h1,…,hN. They want to be sorted in increasing order of height. Bessie can swap any two adjacent cows; one such swap counts as one "swap event". The herd, however, only counts the minimum number of cows whose positions must change for the ordering to become sorted. Output that minimum count, minus 1 (the cows in the herd can re-shuffle among themselves once everyone misplaced has been identified). If the herd is already sorted output 0.
Constraints
- 1 ≤ N ≤ 100.
- 1 ≤ hi ≤ 109, all distinct.
- Time limit: 2s.
Key idea
Compare the input array to its sorted copy. Count the indices i where the values differ — those are the cows that must move. If that count is zero the answer is 0; otherwise the answer is count − 1 (a permutation of k misplaced elements can always be realized with k − 1 swaps because they form cycles whose total swap cost is k − number_of_cycles, and in the worst case the misplaced set is one big cycle).
Complexity target
O(N log N).
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N; cin >> N;
vector<int> a(N);
for (auto& x : a) cin >> x;
vector<int> s = a;
sort(s.begin(), s.end());
int bad = 0;
for (int i = 0; i < N; ++i) if (a[i] != s[i]) ++bad;
cout << (bad == 0 ? 0 : bad - 1) << "\n";
}
Pitfalls
- The −1 is the trick. Beginners report "count of misplaced cows" and get partial credit.
- Already-sorted edge case. Don't print −1 — clamp to 0.
- Heights are distinct, so the sorted target is unique; no need to break ties.
- Don't try to simulate swaps. The count formula is exact for any permutation.
What Bronze January 2018 was actually testing
- P1 — careful rectangle clipping. Recognize the "covers a full side" guarantee and reduce to a single subtraction.
- P2 — brute-force at N ≤ 100. Don't over-engineer; remove-one + sweep is fine.
- P3 — permutation cycle counting in disguise. Misplaced count − 1 (or 0) is the closed form.