USACO 2016 February · Silver · all three problems
Problem 1 — Circular Barn
Statement
n rooms in a ring, each holding exactly one cow at the end. ci cows are queued at the exterior door of room i (Σci = n). Each cow walks clockwise to her assigned room. If she walks d doors, her energy cost is d². Assign cows to rooms to minimize the total squared energy.
Constraints
- 3 ≤ n ≤ 1000.
- 0 ≤ ci; Σci = n.
- Time limit: 2s.
Key idea
Walk once around the ring keeping a queue of "cows in transit." At each door i, push ci new cows onto the queue, then have one cow exit here (the front of the queue, who has walked the farthest). The key insight: starting the sweep at the right door matters. Try each of the n possible starting doors; for each one, simulate the greedy "first-in, first-out" assignment in O(n). With n ≤ 1000, O(n²) ≈ 106 is fine.
Complexity target
O(n²) time, O(n) memory. (Tighter O(n log n) solutions exist but are unnecessary here.)
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n; cin >> n;
vector<int> c(n);
for (auto& x : c) cin >> x;
ll best = LLONG_MAX;
for (int s = 0; s < n; ++s) {
// Simulate sweep starting at door s. queue holds entry positions of cows in transit.
queue<int> q;
ll cost = 0;
bool ok = true;
for (int k = 0; k < n; ++k) {
int door = (s + k) % n;
for (int j = 0; j < c[door]; ++j) q.push(k);
if (q.empty()) { ok = false; break; }
int entry = q.front(); q.pop();
ll d = k - entry;
cost += d * d;
}
if (ok) best = min(best, cost);
}
cout << best << "\n";
}
Pitfalls
- FIFO is optimal. Among cows in transit, always exit the one who entered earliest — that minimizes the longest individual walk, and squared cost is convex so this beats LIFO.
- Not every start works. If cs = 0 you may starve early; mark that sweep as invalid and try another start.
- n² · max(ci)² fits in long long. Don't use int for the running cost.
Problem 2 — Load Balancing
Statement
Same setup as Bronze P3 but bigger: N cows at distinct odd-coordinate points, place a vertical fence at even x = a and a horizontal fence at even y = b. Minimize M = max cows in any of the four quadrants.
Constraints
- 1 ≤ N ≤ 1000.
- Coordinates are positive odd integers ≤ 106.
- Time limit: 2s.
Key idea
Coordinate-compress: only N distinct x-values and N distinct y-values matter, so there are O(N²) candidate (a, b) splits. For each fixed a, sort cows by y and sweep b through the N+1 horizontal candidates, updating four counters incrementally. That gives O(N² log N) overall. Alternative: fix a (N choices), then for each cow compute "left vs right of a," then for each candidate b count by sweep — O(N²) per a, O(N³) total ≈ 109, too slow; the per-a sweep version is the right fit.
Complexity target
O(N² log N) time, O(N) memory. Comfortably under 2s for N ≤ 1000.
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> X(N), Y(N);
for (int i = 0; i < N; ++i) cin >> X[i] >> Y[i];
vector<int> xs = X, ys = Y;
sort(xs.begin(), xs.end()); xs.erase(unique(xs.begin(), xs.end()), xs.end());
sort(ys.begin(), ys.end()); ys.erase(unique(ys.begin(), ys.end()), ys.end());
int best = N;
for (int xv : xs) {
// Fence a = xv - 1 (even). Partition cows by left/right of a.
vector<int> leftY, rightY;
for (int i = 0; i < N; ++i)
(X[i] < xv ? leftY : rightY).push_back(Y[i]);
sort(leftY.begin(), leftY.end());
sort(rightY.begin(), rightY.end());
for (int yv : ys) {
int lb = lower_bound(leftY.begin(), leftY.end(), yv) - leftY.begin();
int rb = lower_bound(rightY.begin(), rightY.end(), yv) - rightY.begin();
int q1 = lb, q2 = (int)leftY.size() - lb;
int q3 = rb, q4 = (int)rightY.size() - rb;
best = min(best, max({q1, q2, q3, q4}));
}
}
cout << best << "\n";
}
Pitfalls
- Don't iterate over raw coordinate values. B can be 106; iterate over the ≤ N distinct cow coordinates instead.
- Sort once per a, not once per (a, b). Re-sorting inside the inner loop kicks you up a factor of N and TLEs.
- Binary search on the right vector. Off-by-one between < b and ≤ b matters; cows are at odd y, fence at even b, so < b is the right predicate.
Problem 3 — Milk Pails
Statement
Two pails of sizes X and Y. At each step you may fill a pail, empty a pail, or pour from one pail into the other (until source empty or destination full). After at most K such operations, output the minimum |M − (x + y)| achievable, where (x, y) are the contents of the two pails.
Constraints
- 1 ≤ X, Y ≤ 100.
- 1 ≤ K ≤ 100.
- 1 ≤ M ≤ 200.
- Time limit: 2s.
Key idea
The state is (x, y, step). There are ≤ (X+1)(Y+1) ≤ 10201 reachable (x, y) pairs and K ≤ 100 steps. BFS from (0, 0): for each state, the 6 transitions are fill-X, fill-Y, empty-X, empty-Y, pour X→Y, pour Y→X. Track the minimum step at which each (x, y) is first reached; if step ≤ K, the state contributes |M − (x+y)| to the answer.
Complexity target
O(X · Y) states, 6 transitions each. ~6·104 operations, instantaneous.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int X, Y, K, M;
cin >> X >> Y >> K >> M;
vector<vector<int>> dist(X+1, vector<int>(Y+1, INT_MAX));
queue<pair<int,int>> q;
dist[0][0] = 0; q.push({0,0});
int best = abs(M - 0);
while (!q.empty()) {
auto [x, y] = q.front(); q.pop();
int d = dist[x][y];
best = min(best, abs(M - (x + y)));
if (d == K) continue;
// 6 transitions
int p = min(X - x, y); // pour Y -> X
int r = min(Y - y, x); // pour X -> Y
pair<int,int> nb[6] = {
{X, y}, {x, Y}, {0, y}, {x, 0},
{x + p, y - p}, {x - r, y + r}
};
for (auto [nx, ny] : nb) {
if (dist[nx][ny] > d + 1) {
dist[nx][ny] = d + 1;
q.push({nx, ny});
}
}
}
cout << best << "\n";
}
Pitfalls
- The empty state (0, 0) counts. You don't have to perform any operations; initialize
best = |M − 0|. - Both pour directions. X→Y and Y→X are distinct transitions; missing one cuts off reachable states.
- BFS layer ≤ K. Stop expanding once a state is at depth K; don't generate K+1 successors.
What Silver February 2016 was actually testing
- P1 — greedy on a ring. FIFO assignment with convex (squared) cost; the brute force over starting doors saves you from a fancier proof.
- P2 — coord-compressed sweep. Same as Bronze P3 but you must drop O(N³) for O(N² log N) by binary-searching y for each fixed x.
- P3 — BFS on (x, y) state. Recognize that the state space is tiny (X·Y) and shortest-path is fundamentally about minimum operations.