USACO 2016 December · Gold · all three problems
Problem 1 — Moocast
Statement
N cows at given 2D positions all use identical walkie-talkies with squared transmission radius X (so a cow can reach another within Euclidean distance √X). Messages can be relayed. Find the minimum integer X such that any cow's message reaches every other cow.
Constraints
- 1 ≤ N ≤ 1000.
- Coordinates are integers up to 25 000.
- Time limit: 2s. Memory limit: 256 MB.
Key idea
The answer is the maximum squared edge weight in a Minimum Spanning Tree on the complete graph of cows (weights = squared distances). MST minimizes the maximum edge that connects all nodes — exactly what "all reachable" means. With N ≤ 1000 there are ~5 · 105 edges; sort + Kruskal with union-find is comfortable.
Complexity target
O(N² log N) for sorting edges; O(N² α(N)) for the union-find scan. ~5 · 106 operations.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct DSU {
vector<int> p, r;
DSU(int n) : p(n), r(n, 0) { iota(p.begin(), p.end(), 0); }
int f(int x) { return p[x] == x ? x : p[x] = f(p[x]); }
bool u(int a, int b) {
a = f(a); b = f(b); if (a == b) return false;
if (r[a] < r[b]) swap(a, b);
p[b] = a; if (r[a] == r[b]) ++r[a];
return true;
}
};
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N; cin >> N;
vector<ll> x(N), y(N);
for (int i = 0; i < N; ++i) cin >> x[i] >> y[i];
// All N*(N-1)/2 edges weighted by squared distance.
vector<tuple<ll, int, int>> e;
e.reserve((ll)N * (N - 1) / 2);
for (int i = 0; i < N; ++i)
for (int j = i + 1; j < N; ++j) {
ll dx = x[i] - x[j], dy = y[i] - y[j];
e.emplace_back(dx * dx + dy * dy, i, j);
}
sort(e.begin(), e.end());
DSU dsu(N);
ll ans = 0; int used = 0;
for (auto& [w, a, b] : e) {
if (dsu.u(a, b)) { ans = w; if (++used == N - 1) break; }
}
cout << ans << "\n";
}
Pitfalls
- Output is squared radius, not the radius. Keep everything in squared distance space — no
sqrt. - Use
long longfor squared distances. Coords up to 25 000 give squares up to 1.25 · 109; pairs of those sum to ~2.5 · 109 — overflows 32-bit signed. - Don't bother with binary search — MST max-edge is cleaner and strictly more general.
- N = 1 edge case. Output 0 (no other cow to reach). The loop naturally returns 0.
Problem 2 — Cow Checklist
Statement
Farmer John must visit H Holsteins (in order 1..H) and G Guernseys (in order 1..G), starting at Holstein 1 and ending at Holstein H. Between consecutive visits he travels in a straight line; cost is the squared Euclidean distance. He may interleave the two ordered lists freely. Find the minimum total cost.
Constraints
- 1 ≤ H, G ≤ 1000.
- Coordinates are integers in [0, 1000].
- Time limit: 2s. Memory limit: 256 MB.
Key idea
DP on (i, j, last) where i = next unused Holstein index, j = next unused Guernsey index, last ∈ {H, G} is which breed the previous step landed on. The current position is fully determined by (i, j, last). Transitions: from state (i, j, H) (we just visited Holstein i−1) you can go to Holstein i (cost dist²(Hi−1, Hi)) leading to (i+1, j, H), or to Guernsey j (cost dist²(Hi−1, Gj)) leading to (i, j+1, G). Initial state: (2, 1, H) with cost 0 (we're at H1). Answer is min over j of dp[H+1][j][H]. The state space is H · G · 2 ≈ 2 · 106.
Complexity target
O(H · G) states with O(1) transitions ≈ 2 · 106 operations.
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 H, G; cin >> H >> G;
vector<ll> hx(H + 1), hy(H + 1), gx(G + 1), gy(G + 1);
for (int i = 1; i <= H; ++i) cin >> hx[i] >> hy[i];
for (int j = 1; j <= G; ++j) cin >> gx[j] >> gy[j];
auto d2 = [&](ll x1, ll y1, ll x2, ll y2) {
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
};
const ll INF = LLONG_MAX / 4;
// dp[i][j][k]: at H_i (k=0) or G_j (k=1), having visited H_1..H_i and G_1..G_j.
vector dp(H + 2, vector(G + 2, array<ll, 2>{INF, INF}));
dp[1][0][0] = 0; // Start at H1, no Guernsey visited yet.
for (int i = 1; i <= H; ++i)
for (int j = 0; j <= G; ++j)
for (int k = 0; k < 2; ++k) if (dp[i][j][k] < INF) {
ll cur = dp[i][j][k];
ll cx = (k == 0 ? hx[i] : gx[j]);
ll cy = (k == 0 ? hy[i] : gy[j]);
// Next visit: Holstein i+1 (must move forward) or Guernsey j+1.
if (i + 1 <= H) {
ll nc = cur + d2(cx, cy, hx[i + 1], hy[i + 1]);
dp[i + 1][j][0] = min(dp[i + 1][j][0], nc);
}
if (j + 1 <= G) {
ll nc = cur + d2(cx, cy, gx[j + 1], gy[j + 1]);
dp[i][j + 1][1] = min(dp[i][j + 1][1], nc);
}
}
ll ans = INF;
for (int j = 0; j <= G; ++j) ans = min(ans, dp[H][j][0]);
cout << ans << "\n";
}
Pitfalls
- Must end at Holstein H — only the (H, *, 0) cells are valid finals.
- Squared distance, not distance. Sum of squares means no
sqrt;long longtotals. - You may skip Guernseys entirely. Visiting zero G's is legal — initial state has j = 0.
- Memory. 1001 · 1001 · 2 = ~2 · 106 64-bit entries ≈ 16 MB; fits but be deliberate (don't accidentally make it 3D
long longwith 4× the size).
Problem 3 — Lasers and Mirrors
Statement
A laser source and a barn sit at integer (x, y) points. There are N fence posts at given (x, y) coordinates, on each of which Bessie may place a 90° mirror. The laser shoots either horizontally or vertically; a mirror redirects it 90° (either turn). Find the minimum number of mirrors needed to route the beam from source to barn, or −1 if impossible.
Constraints
- 1 ≤ N ≤ 105.
- Coordinates are integers up to 109.
- Time limit: 2s. Memory limit: 256 MB.
Key idea
Treat each unique x-coordinate and y-coordinate as a graph node. A fence post at (x, y) creates an edge between the "row y" node and the "column x" node (cost 1 — placing a mirror there switches the beam between rows and columns). The source sits on its row and column for free; the barn must be reached via either its row or its column. BFS from the source row/column gives the minimum number of mirrors minus one (the last mirror puts the beam onto the barn's row/column; the answer is BFS distance − 1 if we treat each fence post as a step between row-node and column-node).
Complexity target
O((N + R + C) + N log N) where R, C are the numbers of distinct y and x values; coordinate compression keeps the graph at most ~2N nodes.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N; long long sx, sy, bx, by;
cin >> N >> sx >> sy >> bx >> by;
vector<long long> px(N), py(N);
for (int i = 0; i < N; ++i) cin >> px[i] >> py[i];
// Compress all distinct x's and y's (including source and barn) into IDs.
vector<long long> xs = {sx, bx}, ys = {sy, by};
for (int i = 0; i < N; ++i) { xs.push_back(px[i]); ys.push_back(py[i]); }
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());
auto xid = [&](long long v){ return (int)(lower_bound(xs.begin(), xs.end(), v) - xs.begin()); };
auto yid = [&](long long v){ return (int)(lower_bound(ys.begin(), ys.end(), v) - ys.begin()); };
// Node layout: columns 0..|xs|-1, rows |xs|..|xs|+|ys|-1.
int XS = xs.size(), YS = ys.size();
auto col = [&](int i){ return i; };
auto row = [&](int i){ return XS + i; };
vector<vector<int>> adj(XS + YS);
for (int i = 0; i < N; ++i) {
int c = col(xid(px[i])), r = row(yid(py[i]));
adj[c].push_back(r); adj[r].push_back(c);
}
// BFS from both source's row and column; goal is barn's row or column.
vector<int> dist(XS + YS, -1);
queue<int> q;
int sCol = col(xid(sx)), sRow = row(yid(sy));
int bCol = col(xid(bx)), bRow = row(yid(by));
dist[sCol] = 0; q.push(sCol);
dist[sRow] = 0; q.push(sRow);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : adj[u]) if (dist[v] == -1) { dist[v] = dist[u] + 1; q.push(v); }
}
int a = dist[bCol], b = dist[bRow];
int best = -1;
if (a != -1) best = a;
if (b != -1) best = (best == -1) ? b : min(best, b);
// BFS distance counts mirror hops; subtract 1 because reaching the
// barn's own row/column needs no extra mirror at the barn itself.
cout << (best <= 0 ? best : best - 1) << "\n";
}
Pitfalls
- Source and barn share row/column without a mirror. The source contributes 0-distance to both its row and column nodes; if the barn is on either, the answer is 0.
- Subtract one mirror at the end. Each fence post visit is a 90° turn; reaching the barn's row or column means the last 90° turn already aimed at the barn — verify the −1 against the sample.
- Coordinate compression is mandatory. Raw coordinates up to 109 can't be array-indexed.
- Output −1 when unreachable. Both BFS distances stay at −1.
What Gold December 2016 was actually testing
- P1 — bottleneck graph / MST max edge. Minimizing the worst edge across a spanning structure is exactly Kruskal.
- P2 — 2D interleave DP. Classic "merge two ordered sequences" DP with O(H · G) states.
- P3 — rows-and-columns bipartite graph + BFS. Mirrors are edges between row-nodes and column-nodes; minimum mirrors = BFS distance minus a boundary correction.