USACO 2017 February · Silver · all three problems
Problem 1 — Why Did the Cow Cross the Road
Statement
There are C chickens, each available at a single time Ti, and N cows, each with an availability window [Aj, Bj]. A chicken can help cow j iff Aj ≤ Ti ≤ Bj. Each chicken helps at most one cow and each cow uses at most one chicken. Output the maximum number of pairings.
Constraints
- 1 ≤ C, N ≤ 20,000.
- 0 ≤ Ti, Aj, Bj ≤ 109.
- Time limit: 2s.
Key idea
Classic interval-stabbing greedy. Sort cows by right endpoint Bj ascending. Sort the chicken times. Use a multiset of chicken times; for each cow in order, find the smallest available chicken time ≥ Aj. If it's ≤ Bj, pair them and erase the chicken. This is the standard "earliest-deadline-first" exchange argument: a cow with the earliest deadline gets the cheapest still-feasible chicken.
Complexity target
O((C + N) log C).
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int C, N; cin >> C >> N;
multiset<int> chickens;
for (int i = 0; i < C; ++i) {
int t; cin >> t; chickens.insert(t);
}
vector<pair<int,int>> cows(N); // (B, A) so we sort by deadline
for (auto& [b, a] : cows) cin >> a >> b;
sort(cows.begin(), cows.end());
int paired = 0;
for (auto [b, a] : cows) {
auto it = chickens.lower_bound(a);
if (it != chickens.end() && *it <= b) {
chickens.erase(it);
++paired;
}
}
cout << paired << "\n";
}
Pitfalls
- Sort by deadline B, not by start A. Sorting by A gives wrong answers — earliest-deadline-first is the right exchange invariant.
- Use a multiset of chicken times. Times can repeat in principle;
setdrops duplicates. - Inclusive endpoints. The compatibility check is A ≤ T ≤ B (both sides closed).
Problem 2 — Why Did the Cow Cross the Road II
Statement
N traffic signals along a road are numbered 1..N. B of them are broken. Find the minimum number of broken signals you must repair so that there is at least one contiguous block of K consecutive working signals.
Constraints
- 1 ≤ N ≤ 105.
- 1 ≤ K ≤ N, 0 ≤ B ≤ N.
- Time limit: 2s.
Key idea
Mark broken positions in a boolean array. The answer is the minimum number of broken signals inside any length-K window over [1..N]. Sliding-window sum in O(N).
Complexity target
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, K, B; cin >> N >> K >> B;
vector<int> broken(N + 2, 0);
for (int i = 0; i < B; ++i) {
int x; cin >> x; broken[x] = 1;
}
// Sliding window of size K over [1..N].
int cur = 0;
for (int i = 1; i <= K; ++i) cur += broken[i];
int best = cur;
for (int i = K + 1; i <= N; ++i) {
cur += broken[i] - broken[i - K];
best = min(best, cur);
}
cout << best << "\n";
}
Pitfalls
- 1-indexed signals. Allocate size N+2 to avoid off-by-one when the window slides off the end.
- K can equal N. The whole row is the only window; answer = B.
- K can equal 1. If any signal is working, the answer is 0; the sliding window correctly returns 0.
Problem 3 — Why Did the Cow Cross the Road III
Statement
An N×N grid of fields. R road segments separate adjacent fields. K cows stand at given cells. A pair of cows is "distant" if you cannot travel from one to the other across grid edges without crossing a road. Output the number of distant pairs.
Constraints
- 2 ≤ N ≤ 100.
- 1 ≤ K ≤ 100, K ≤ N².
- 0 ≤ R ≤ ~105.
- Time limit: 2s.
Key idea
Build an undirected graph on the N² cells. Connect (r,c) to its 4 neighbors except where a road separates them. Flood-fill components. For each cow record its component id. Group cows by component; if a component holds ci cows, the number of connected pairs is Σ C(ci,2). Distant pairs = C(K,2) − connected pairs.
Complexity target
O(N² + R + K). N=100 so N² = 104.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int N;
int idx(int r, int c) { return r * N + c; }
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int K, R; cin >> N >> K >> R;
// blocked[a][b] = true if cell a and cell b have a road between them.
set<pair<int,int>> blocked;
for (int i = 0; i < R; ++i) {
int r1, c1, r2, c2; cin >> r1 >> c1 >> r2 >> c2;
int a = idx(r1 - 1, c1 - 1), b = idx(r2 - 1, c2 - 1);
blocked.insert({min(a,b), max(a,b)});
}
vector<int> comp(N * N, -1);
int cid = 0;
int dr[4] = {-1, 1, 0, 0}, dc[4] = {0, 0, -1, 1};
for (int s = 0; s < N * N; ++s) if (comp[s] == -1) {
queue<int> q; q.push(s); comp[s] = cid;
while (!q.empty()) {
int u = q.front(); q.pop();
int r = u / N, c = u % N;
for (int k = 0; k < 4; ++k) {
int nr = r + dr[k], nc = c + dc[k];
if (nr < 0 || nr >= N || nc < 0 || nc >= N) continue;
int v = idx(nr, nc);
if (blocked.count({min(u,v), max(u,v)})) continue;
if (comp[v] != -1) continue;
comp[v] = cid; q.push(v);
}
}
++cid;
}
map<int, ll> cnt;
for (int i = 0; i < K; ++i) {
int r, c; cin >> r >> c; cnt[comp[idx(r - 1, c - 1)]]++;
}
ll connected = 0;
for (auto [_, v] : cnt) connected += v * (v - 1) / 2;
ll total = (ll)K * (K - 1) / 2;
cout << total - connected << "\n";
}
Pitfalls
- Roads block edges, not cells. Store the road as the unordered pair of the two cells it separates.
- Multiple cows can share a cell. The constraint K ≤ N² doesn't forbid duplicate positions — count them, don't dedupe.
- Total minus connected. Computing "distant" directly via BFS-from-each-cow is O(K · N²) and works at K=100, but the complement formula is cleaner.
What Silver February 2017 was actually testing
- P1 — interval-stabbing greedy. Sort by deadline, pick smallest feasible point per interval. The bread-and-butter Silver pattern.
- P2 — sliding window. The whole problem is "min of length-K window sums". Recognize it; don't binary search.
- P3 — flood fill on a grid with edge obstructions. Standard BFS/DSU, then a pairs-by-component count.