USACO 2017 December · Bronze · all three problems
Problem 1 — Blocked Billboard
Statement
A rectangular billboard sits on a 2-D plane with corners at (x1, y1) and (x2, y2). A truck, also a rectangle, drives by and may cover part (or all) of the billboard. If the truck completely covers the billboard along some axis-aligned direction, the visible portion of the billboard is the original area minus the overlap. Otherwise the billboard is unobstructed. Output the visible area. (The problem actually gives two billboards and one truck — visible area is each billboard's area minus the truck's overlap with it, summed.)
Constraints
- All coordinates are integers in [−1000, 1000].
- Each rectangle has positive area: x1 < x2 and y1 < y2.
- Time limit: 2s.
Key idea
For two axis-aligned rectangles A = [ax1, ax2] × [ay1, ay2] and B similar, the overlap rectangle has x-extent [max(ax1, bx1), min(ax2, bx2)] and y-extent analogous. The overlap area is the product of those extents if both are positive, else 0. Visible area of A given truck T = area(A) − area(A ∩ T). Sum over the two billboards.
Complexity target
O(1). The whole problem is a fixed-size arithmetic identity.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
struct R { int x1, y1, x2, y2; };
int area(const R& r) { return (r.x2 - r.x1) * (r.y2 - r.y1); }
int overlap(const R& a, const R& b) {
int xl = max(a.x1, b.x1), xr = min(a.x2, b.x2);
int yl = max(a.y1, b.y1), yr = min(a.y2, b.y2);
if (xr <= xl || yr <= yl) return 0;
return (xr - xl) * (yr - yl);
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
R a, b, t;
cin >> a.x1 >> a.y1 >> a.x2 >> a.y2;
cin >> b.x1 >> b.y1 >> b.x2 >> b.y2;
cin >> t.x1 >> t.y1 >> t.x2 >> t.y2;
int ans = (area(a) - overlap(a, t)) + (area(b) - overlap(b, t));
cout << ans << "\n";
}
Pitfalls
- Empty intersection. When the rectangles don't actually overlap, the candidate width or height can be ≤ 0 — return 0, don't multiply two negatives.
- Touching edges don't count as overlap. Use strict
<=on the empty test. - Don't try to subtract two truck rectangles. There's one truck; the two billboards are independent.
- Coordinates can be negative. Min/max work fine; just don't assume positive.
Problem 2 — The Bovine Shuffle
Statement
N cows stand in positions 1..N. A shuffle is described by a permutation a1..aN: the cow currently at position i moves to position ai. The shuffle is applied three times. Given the final ordering of cows (the IDs at each position after three shuffles), recover and print the original ordering.
Constraints
- 1 ≤ N ≤ 100.
- a is a permutation of 1..N.
- Time limit: 2s.
Key idea
Three forward applications of permutation a correspond to one application of a3. To invert, apply the inverse permutation three times to the final array: for each position i, the cow there came from position a−1(i) one step earlier, and so on. Equivalently, build b where b[a[i]] = final[i], then apply that step three times.
Complexity target
O(N) per shuffle step, three steps total → O(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 + 1), cur(N + 1);
for (int i = 1; i <= N; ++i) cin >> a[i];
for (int i = 1; i <= N; ++i) cin >> cur[i]; // ordering after 3 shuffles
// Invert the shuffle three times.
// One forward shuffle: nxt[a[i]] = cur[i].
// So one backward shuffle: prv[i] = cur[a[i]].
for (int step = 0; step < 3; ++step) {
vector<int> prv(N + 1);
for (int i = 1; i <= N; ++i) prv[i] = cur[a[i]];
cur = prv;
}
for (int i = 1; i <= N; ++i) cout << cur[i] << "\n";
}
Pitfalls
- Direction of the shuffle. "Cow at i moves to a[i]" means new[a[i]] = old[i]. Confusing this with new[i] = old[a[i]] gives the inverse problem.
- Three shuffles, not one. Don't forget the loop.
- 1-indexed input. USACO positions are 1..N; using a 0-indexed array off-by-ones the permutation read.
Problem 3 — Milk Measurement
Statement
Farmer John has 3 cows, each starting at 7 gallons/day. Each day in chronological order there is one event: cow c gains or loses some milk. After each event, determine the current set of top-producing cows (those tied for max). Count the number of days the set of top producers changes from the previous day.
Constraints
- 1 ≤ N ≤ 100 events (Bronze version).
- Cow IDs are from {7, 9, 11} ; deltas are integers in roughly [−100, 100].
- Events are given by day; days are distinct and unsorted in the input — sort by day first.
- Time limit: 2s.
Key idea
Sort events by day. Maintain a map cow → current production (initial 7). After each event, recompute the set of cows tied for the max. Compare to the previous set; if different, increment the answer. The "previous" before any events is {all three cows, since all tie at 7}.
Complexity target
O(N log N) for sorting; O(1) per event since there are only 3 cows.
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<tuple<int,int,int>> ev(N); // day, cow, delta
for (auto& [d, c, x] : ev) cin >> d >> c >> x;
sort(ev.begin(), ev.end());
map<int,int> prod = {{7, 7}, {9, 7}, {11, 7}};
auto topSet = [&]() {
int m = INT_MIN;
for (auto& [k,v] : prod) m = max(m, v);
set<int> s;
for (auto& [k,v] : prod) if (v == m) s.insert(k);
return s;
};
auto prevTop = topSet();
int ans = 0;
for (auto& [d, c, x] : ev) {
prod[c] += x;
auto cur = topSet();
if (cur != prevTop) { ++ans; prevTop = cur; }
}
cout << ans << "\n";
}
Pitfalls
- Input is unsorted. Events come in arbitrary day order — sort first or the leader history is wrong.
- Set-equality, not "did the max change". Two cows tied at the top then one dropping out still counts even if the max value is unchanged.
- Initial state has all three tied. The "previous" baseline matters for event 1.
- Negative deltas. A cow's production can drop below others; the max can decrease.
What Bronze December 2017 was actually testing
- P1 — rectangle overlap formula. The single most-tested geometric primitive in Bronze; memorize the min/max identity.
- P2 — permutation composition and inversion. Translating "cow at i moves to a[i]" into array index math is the whole exercise.
- P3 — event simulation with a tie-aware leader set. Sort, simulate, compare sets — clean for-loop work.