USACO 2015 December · Silver · all three problems
Problem 1 — Switching on the Lights
Statement
Bessie starts in room (1,1) of an N×N grid. All rooms are initially dark except (1,1). Each room may contain switches that, when entered, turn lights on in specific other rooms (each switch toggles a specific room on; once on it stays on). Bessie can only walk between adjacent rooms (up/down/left/right) that are currently lit. How many rooms can eventually be illuminated?
Constraints
- 2 ≤ N ≤ 100.
- 1 ≤ M ≤ 20,000 switch entries.
- Time limit: 2s. Memory limit: 256 MB.
Key idea
Iterate to a fixed point. Maintain (a) lit[r][c] and (b) the set of rooms Bessie can reach (lit rooms connected to (1,1) by lit rooms). On each pass, BFS from (1,1) through lit rooms; for every reachable room, fire all its switches (turning new rooms lit). A newly lit room becomes reachable if it's adjacent (4-neighbor) to a reachable room — so re-BFS. Loop until no new room is lit in a full pass. With N ≤ 100 and M ≤ 20,000 the total work is bounded.
Complexity target
O((N² + M) · N²) worst case; well under a second.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int N, M;
vector<pair<int,int>> sw[105][105]; // switches in (r,c)
bool lit[105][105], reach[105][105];
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N >> M;
for (int i = 0; i < M; ++i) {
int x, y, a, b; cin >> x >> y >> a >> b;
sw[x][y].push_back({a, b});
}
lit[1][1] = true;
int dr[] = {1,-1,0,0}, dc[] = {0,0,1,-1};
bool changed = true;
while (changed) {
changed = false;
memset(reach, 0, sizeof reach);
queue<pair<int,int>> q; q.push({1,1}); reach[1][1] = true;
while (!q.empty()) {
auto [r, c] = q.front(); q.pop();
for (auto [a, b] : sw[r][c]) if (!lit[a][b]) { lit[a][b] = true; changed = true; }
for (int d = 0; d < 4; ++d) {
int nr = r + dr[d], nc = c + dc[d];
if (nr<1||nc<1||nr>N||nc>N) continue;
if (lit[nr][nc] && !reach[nr][nc]) { reach[nr][nc] = true; q.push({nr,nc}); }
}
}
}
int ans = 0;
for (int r = 1; r <= N; ++r) for (int c = 1; c <= N; ++c) if (lit[r][c]) ++ans;
cout << ans << "\n";
}
Pitfalls
- Lit ≠ reachable. A switched-on room far from (1,1) doesn't count toward Bessie's reach until a lit path connects it.
- Don't BFS once. Lighting new rooms opens new corridors; you must iterate to fixed point.
- Count lit rooms, not reachable rooms. The problem asks how many rooms have their light on at the end — read carefully.
- 1-indexed coordinates. The statement uses (1,1) start.
Problem 2 — High Card Wins
Statement
A deck has 2N cards numbered 1…2N. Elsie holds some N of them; Bessie has the rest. They play N rounds: each round Elsie reveals her next card (Elsie's order is known in advance), and Bessie simultaneously plays a card from her hand. The higher card wins the round. Bessie may permute her hand as she likes. Output the maximum number of rounds Bessie can win.
Constraints
- 1 ≤ N ≤ 50,000.
- Time limit: 2s. Memory limit: 256 MB.
Key idea
Classic two-pointer greedy. Sort Bessie's hand ascending. Walk through Elsie's plays in the given order; but it's easier to also sort Elsie's plays ascending and pair greedily: for each Elsie card (smallest first), assign Bessie's smallest unused card that still beats it; if none, this Elsie card is unwinnable and Bessie throws her smallest remaining card at a later round. Equivalent to the standard "horse race" problem.
Complexity target
O(N log N) sort + O(N) two-pointer.
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> elsie(N);
vector<bool> taken(2*N + 1, false);
for (auto& e : elsie) { cin >> e; taken[e] = true; }
vector<int> bessie;
bessie.reserve(N);
for (int v = 1; v <= 2*N; ++v) if (!taken[v]) bessie.push_back(v);
sort(elsie.begin(), elsie.end());
sort(bessie.begin(), bessie.end());
// For each Elsie card (ascending), use Bessie's smallest card > it.
int wins = 0, j = 0;
for (int i = 0; i < N; ++i) {
while (j < N && bessie[j] < elsie[i]) ++j;
if (j < N && bessie[j] > elsie[i]) { ++wins; ++j; }
}
cout << wins << "\n";
}
Pitfalls
- Order doesn't matter for the count. Because Bessie can permute, the max-wins answer ignores Elsie's order.
- Ties don't win. Cards are distinct (1..2N), so this never happens here — but the comparison is strict >.
- Use the "smallest card that still beats" greedy. Saving your biggest cards for Elsie's biggest cards is suboptimal.
- Don't construct Bessie's hand naively from "remove Elsie's set" — use a boolean mark to stay O(N).
Problem 3 — Breed Counting
Statement
N cows stand in a line, each tagged with breed 1 (Holstein), 2 (Guernsey), or 3 (Jersey). Q queries of the form (a, b) ask how many cows of each breed are in positions a..b inclusive. Output three counts per query.
Constraints
- 1 ≤ N, Q ≤ 100,000.
- Breeds are 1, 2, or 3.
- Time limit: 2s. Memory limit: 256 MB.
Key idea
Three prefix-sum arrays, one per breed. prek[i] = number of cows of breed k in positions 1..i. Each query (a, b) returns prek[b] − prek[a−1] for k = 1,2,3 in O(1).
Complexity target
O(N + Q).
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N, Q; cin >> N >> Q;
vector<array<int,4>> pre(N + 1, {0,0,0,0}); // index 0 unused for breeds
for (int i = 1; i <= N; ++i) {
int b; cin >> b;
pre[i] = pre[i-1];
pre[i][b]++;
}
while (Q--) {
int a, b; cin >> a >> b;
cout << (pre[b][1] - pre[a-1][1]) << ' '
<< (pre[b][2] - pre[a-1][2]) << ' '
<< (pre[b][3] - pre[a-1][3]) << "\n";
}
}
Pitfalls
- 1-indexed positions. Query is [a, b] inclusive; use pre[b] − pre[a−1].
- Don't BIT/segment-tree this. No updates → prefix sums are the right tool.
- Fast I/O. 10⁵ queries × 3 ints = 3·10⁵ tokens; use sync_with_stdio(false) and "\n" not endl.
- Output order is fixed. Counts for breed 1, then 2, then 3 — always.
What Silver December 2015 was actually testing
- P1 — iterated BFS to fixed point. Recognize that reachability and lighting affect each other.
- P2 — greedy assignment on sorted hands. The classic "horse race" pattern.
- P3 — prefix sums. Three independent prefix arrays, O(1) per query.