USACO 2016 January · Silver · all three problems
Problem 1 — Angry Cows
Statement
N hay bales lie at integer positions on a number line. You have K cows of identical "power" R; each cow lands at any real position and detonates every bale within distance R (no chain reaction — Silver version is simple coverage). Find the minimum R such that K well-placed cows can cover every bale.
Constraints
- 1 ≤ N ≤ 50,000, 1 ≤ K ≤ 10.
- 0 ≤ xi ≤ 109, distinct integers.
- Answer R is an integer.
- Time limit: 2s.
Key idea
Binary search on R. For a candidate R, sort bales and greedily cover: place the first cow at x1 + R (covering [x1, x1 + 2R]); skip all bales ≤ x1 + 2R; repeat from the next uncovered bale. Feasible iff total cows used ≤ K. Binary search R over [0, max_x − min_x].
Complexity target
O(N log N) for the sort, O(N) per feasibility check, O(log(109)) binary search iterations ⇒ O(N log V).
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int N, K;
vector<long long> X;
bool ok(long long R) {
// Greedy: each cow covers [start, start + 2R]; place at first uncovered bale + R.
int cows = 0;
for (int i = 0; i < N; ) {
++cows;
if (cows > K) return false;
long long limit = X[i] + 2 * R;
while (i < N && X[i] <= limit) ++i;
}
return cows <= K;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N >> K; X.resize(N);
for (auto& x : X) cin >> x;
sort(X.begin(), X.end());
long long lo = 0, hi = X.back() - X.front();
while (lo < hi) {
long long mid = (lo + hi) / 2;
if (ok(mid)) hi = mid; else lo = mid + 1;
}
cout << lo << "\n";
}
Pitfalls
- Cow position is real. Place at xleftmost + R, not at a bale, to cover a 2R-wide window.
- Greedy after sorting is optimal. Earliest leftmost cow always extends the rightmost cover.
- Binary search bounds. R = 0 may be feasible if N ≤ K; cap hi at max−min, never larger.
- Integer arithmetic. R is integer per statement, so no floating point needed.
Problem 2 — Subsequences Summing to Sevens
Statement
Given N cow IDs in a row (non-negative integers), find the length of the longest contiguous sub-array whose sum is divisible by 7. Output 0 if no such non-empty sub-array exists.
Constraints
- 1 ≤ N ≤ 50,000.
- 0 ≤ IDi ≤ 106.
- Use 64-bit arithmetic for prefix sums.
- Time limit: 2s.
Key idea
Standard "longest sub-array sum ≡ 0 (mod m)" trick. Let Pi = (id1 + … + idi) mod 7. The sub-array (l, r] sums to 0 mod 7 iff Pr = Pl. To maximise length, for each residue r ∈ {0,…,6} remember the earliest index where it appears; for residue 0 the earliest index is 0 (empty prefix). For each i, answer candidate is i − firstSeen[Pi].
Complexity target
O(N) one pass.
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;
// first[r] = smallest prefix index i where P_i % 7 == r; -1 if unseen.
array<int, 7> first; first.fill(-1);
first[0] = 0; // empty prefix has residue 0
long long P = 0;
int best = 0;
for (int i = 1; i <= N; ++i) {
long long x; cin >> x;
P = (P + x) % 7;
if (first[P] == -1) first[P] = i;
else best = max(best, i - first[P]);
}
cout << best << "\n";
}
Pitfalls
- Empty prefix is residue 0. If you forget to seed first[0] = 0, you'll miss any sub-array starting at index 1.
- Take earliest, not latest. You want longest length ⇒ earliest matching prefix.
- Use mod-7 throughout to avoid overflow even though 50,000 · 10⁶ = 5·1010 fits in 64-bit anyway.
- Output 0 when no answer. Don't accidentally output a length that's the whole array unless its sum mod 7 is 0.
Problem 3 — Build Gates
Statement
Farmer John starts at (0,0) and walks N unit steps over the cardinal directions, laying a fence as he goes. The fence may cross or overlap itself. Walking partitions the plane into regions (the fence itself plus the connected components of the complement). Output the minimum number of gates needed so that, treating each gate as a single hole in the fence, every region is connected to every other.
Constraints
- 1 ≤ N ≤ 1000.
- Path string of length N over {N,E,S,W}.
- Time limit: 2s.
Key idea
The number of regions minus 1 is exactly the number of gates needed (spanning-tree of the planar region graph). By Euler's formula on the planar graph induced by the fence segments, F = E − V + 1 + C, where V, E are vertices and edges of the graph after subdividing at every intersection, C is the number of connected components, and F counts bounded faces (= regions minus the outer face, plus the outer face — so total regions = F + 1 actually = E − V + 1 + C + 1). Practically: blow each grid cell up to 3×3 sub-cells, mark fence edges between sub-cells, then flood-fill the open sub-cells to count regions; subtract 1.
Complexity target
O((N · 3)2) flood fill on a 3000×3000 sub-grid is too big; instead bound by the bounding box of the path. Total visited cells ≤ N, so flood fill is O(N²) at worst, fine 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; string s; cin >> N >> s;
// Blow-up: map each integer cell (x,y) to a 3x3 block (3x+1, 3y+1) as its center,
// (3x, 3y+1) west edge, (3x+2, 3y+1) east edge, etc. Walk the path and mark
// fence sub-cells. Flood-fill the unmarked cells inside a bounding box (with
// a 1-cell margin so the outer region is reachable).
int x = 0, y = 0, minX = 0, maxX = 0, minY = 0, maxY = 0;
vector<pair<int,int>> pts; pts.push_back({0, 0});
for (char c : s) {
if (c == 'N') ++y; else if (c == 'S') --y;
else if (c == 'E') ++x; else --x;
pts.push_back({x, y});
minX = min(minX, x); maxX = max(maxX, x);
minY = min(minY, y); maxY = max(maxY, y);
}
int W = 3 * (maxX - minX + 1) + 2;
int H = 3 * (maxY - minY + 1) + 2;
auto idx = [&](int X, int Y) { return Y * W + X; };
vector<char> fence(W * H, 0);
auto mark = [&](int gx, int gy) {
int X = 3 * (gx - minX) + 1 + 1, Y = 3 * (gy - minY) + 1 + 1;
fence[idx(X, Y)] = 1;
};
mark(pts[0].first, pts[0].second); // center vertex of starting cell
for (size_t i = 1; i < pts.size(); ++i) {
// mark the edge between pts[i-1] and pts[i]
int X1 = 3*(pts[i-1].first - minX)+1+1, Y1 = 3*(pts[i-1].second - minY)+1+1;
int X2 = 3*(pts[i ].first - minX)+1+1, Y2 = 3*(pts[i ].second - minY)+1+1;
fence[idx((X1+X2)/2, (Y1+Y2)/2)] = 1;
fence[idx(X2, Y2)] = 1;
}
// Flood-fill unmarked components.
vector<char> vis(W * H, 0);
int regions = 0;
int dx[4] = {1,-1,0,0}, dy[4] = {0,0,1,-1};
for (int Y = 0; Y < H; ++Y) for (int X = 0; X < W; ++X) {
if (fence[idx(X, Y)] || vis[idx(X, Y)]) continue;
++regions;
queue<pair<int,int>> q; q.push({X, Y}); vis[idx(X, Y)] = 1;
while (!q.empty()) {
auto [a, b] = q.front(); q.pop();
for (int k = 0; k < 4; ++k) {
int na = a + dx[k], nb = b + dy[k];
if (na < 0 || na >= W || nb < 0 || nb >= H) continue;
if (fence[idx(na, nb)] || vis[idx(na, nb)]) continue;
vis[idx(na, nb)] = 1; q.push({na, nb});
}
}
}
cout << regions - 1 << "\n";
}
Pitfalls
- 3×3 blow-up is the canonical trick. A direct flood-fill on the original grid can't distinguish "edge between two cells" from "cell wall through a vertex".
- Pad the bounding box. Add a 1-cell margin so the outer region is one connected component (otherwise diagonals can pinch it into multiple "outsides").
- Answer is regions − 1. One gate per pair of adjacent regions in a spanning tree of the region adjacency graph.
- Re-walked segments don't add regions. Use a set/flag — re-marking a fence sub-cell is a no-op.
What Silver January 2016 was actually testing
- P1 — binary search on the answer. Classic "minimise max-covered-distance with K resources" greedy feasibility.
- P2 — prefix sums mod m. The textbook "earliest residue index" pattern.
- P3 — planar regions via flood-fill on a 3× blow-up. Silver-level geometric counting; the trap is the blow-up factor, not the BFS.