USACO 2023 December · Silver · all three problems
Problem 1 — Bovine Acrobatics
Statement
Farmer John has cows in N distinct weight classes; class i has ai cows of weight wi. He builds at most M towers; in a tower, each cow above another must weigh at least K less than the cow beneath it. Maximize the total number of cows used across all towers.
Constraints
- 1 ≤ N ≤ 2×105; 1 ≤ M ≤ 109; 0 ≤ K ≤ 109.
- 1 ≤ ai, wi ≤ 109; total cows fit in 64-bit.
- Time limit: 2s.
Key idea
Sort weights ascending. Sweep through classes maintaining a queue of "available capacity": when you reach weight class i, every tower whose current top has weight ≤ wi − K is eligible to receive one of the new cows. Greedily place min(ai, eligible capacity + spare new towers) cows, where spare new towers = M − (towers in use). Each placement uses one tower-slot; bookkeep with a queue of (weight, count) pairs of currently-top cows.
Complexity target
O(N log N) for the sort; O(N) sweep.
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; ll M, K;
cin >> N >> M >> K;
vector<pair<ll, ll>> cows(N); // (weight, count)
for (auto& [w, a] : cows) cin >> w >> a;
sort(cows.begin(), cows.end());
queue<pair<ll, ll>> available; // tops whose weight allows stacking, FIFO by weight
ll towersInUse = 0, ans = 0;
for (auto [w, a] : cows) {
// Move newly-eligible tops into the "available" pool.
// (Since we sweep in increasing w, anything with top ≤ w − K is eligible.)
ll eligible = 0;
// Re-scan from the front of `available`: all entries with weight ≤ w − K stay eligible.
// Sum them.
queue<pair<ll, ll>> keep;
while (!available.empty()) {
auto [tw, tc] = available.front(); available.pop();
if (tw <= w - K) eligible += tc;
else keep.push({tw, tc});
}
available = keep;
ll spareNew = M - towersInUse; // can also start new towers
ll place = min(a, eligible + spareNew);
ans += place;
ll useNew = max<ll>(0, place - eligible);
towersInUse += useNew;
// Every placed cow becomes a new "top" at weight w.
available.push({w, place});
}
cout << ans << "\n";
}
Pitfalls
- K can be 0. Then every top with weight ≤ w is eligible — including same-weight tops, so the same class can stack on itself.
- M up to 109. You cannot keep one entry per tower; collapse equal weights.
- The queue rescan above is O(N) amortized because each (weight,count) entry is consumed once, but rebuilding into
keepwith the same structure is needed to preserve order — or use a pointer / deque. - Don't sort by count. Sorting must be by weight.
Problem 2 — Cycle Correspondence
Statement
Two people independently number N barns with labels 1..N. Each picks the same set of K barns and lays out a cycle on them in some cyclic order. Given the two cyclic sequences (each a permutation of K of the labels 1..N), maximize the number of barns that can receive the same label under both schemes — i.e. choose a single global relabeling that "agrees" with both cyclic structures on as many barns as possible.
Constraints
- 3 ≤ K ≤ N ≤ 5×105.
- Time limit: 2s. Memory: 256 MB.
Key idea
A cycle of length K admits 2K symmetries (K rotations × 2 reflections). For each of the 2K alignments of cycle A onto cycle B, count fixed points (barns whose label matches). The answer is max over alignments. Brute force is O(K2), too slow at K=5·105. Build the permutation that maps "position in A" → "position in B", then for each rotation r, count i with B[(i+r) mod K] = A[i]; that's a circular convolution / FFT, or a clever counting argument with a difference array on the cyclic shift values.
Complexity target
O(K log K) with FFT, or O(N + K) with a difference-array trick on shift counts.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N, K; cin >> N >> K;
vector<int> A(K), B(K);
for (auto& x : A) cin >> x;
for (auto& x : B) cin >> x;
// posB[label] = index of that label in B's cycle (only for labels appearing in B).
vector<int> posB(N + 1, -1);
for (int i = 0; i < K; ++i) posB[B[i]] = i;
// For each i in A's cycle, if A[i] also appears in B at position p = posB[A[i]],
// then a rotation of r = (p - i) mod K aligns this label across both cycles.
auto best = [&](bool reflect) {
vector<int> cnt(K, 0);
for (int i = 0; i < K; ++i) {
int lab = A[i];
int p = posB[lab];
if (p < 0) continue;
int j = reflect ? (K - 1 - i) : i;
int r = ((p - j) % K + K) % K;
cnt[r]++;
}
return *max_element(cnt.begin(), cnt.end());
};
cout << max(best(false), best(true)) << "\n";
}
Pitfalls
- Reflection. Cycles read in either direction are the same cycle — don't forget the reversed alignment.
- Labels outside both cycles. The N − K barns not in the cycles can always be made to match; whether they count toward the answer depends on the exact problem statement — re-read carefully.
- K can equal N. Don't allocate cnt of size N; size K is correct.
- Modular arithmetic on negatives. Use
((x % K) + K) % K.
Problem 3 — Target Practice
Statement
Bessie the robot vine starts at position 0 and executes a string of C commands: L moves
her one unit left, R one unit right, F fires (hitting whatever target sits
at her current position; each target can be hit at most once). T distinct targets sit at integer
positions in [−C, C]. You may change at most one command in the string before execution. Maximize the
number of targets hit.
Constraints
- 1 ≤ T, C ≤ 105.
- Time limit: 2s.
- Inputs 4–6: T, C ≤ 1000 (brute force passes); inputs 7–15: full range.
Key idea
Simulate the original string once, record the position pj after each step. A "change at step k from old → new" shifts all subsequent positions: if old=L, new=R, positions j ≥ k shift by +2; if old=R, new=L, by −2; L↔F or R↔F shifts by ±1; F↔F is a no-op. For each (k, old→new) we want to know: with the shifted trajectory, how many distinct targets get hit by the F-commands? Encode each "would hit target at q" as the multiset of (step j, position pj) at F-steps; after the shift, the F at step j hits position pj + δ if j > k, else pj. Bucket Fs by whether they happen before or after the change point and use prefix/suffix counts indexed by position.
Complexity target
O((T + C) · constant). Each of O(C) candidate changes is processed in amortized O(1) using precomputed histograms over [−C, C].
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int T, C; cin >> T >> C;
string cmd; cin >> cmd;
vector<int> tgt(T);
set<int> targets;
for (auto& x : tgt) { cin >> x; targets.insert(x); }
// Simulate original positions.
vector<int> pos(C + 1, 0);
for (int i = 0; i < C; ++i) {
pos[i + 1] = pos[i] + (cmd[i] == 'L' ? -1 : cmd[i] == 'R' ? 1 : 0);
}
auto countHits = [&](int changePos, char newCh) {
// O(C) — fine for the small subtask, replace with a histogram for full.
set<int> hit;
int p = 0;
for (int i = 0; i < C; ++i) {
char c = (i == changePos) ? newCh : cmd[i];
if (c == 'L') --p;
else if (c == 'R') ++p;
else if (targets.count(p)) hit.insert(p);
}
return (int)hit.size();
};
int best = countHits(-1, 'F'); // no change
for (int i = 0; i < C; ++i)
for (char nc : {'L', 'R', 'F'})
if (cmd[i] != nc) best = max(best, countHits(i, nc));
cout << best << "\n";
// Full-credit solution replaces countHits with a precomputed
// count-of-F-hits-per-shift histogram in O(C). [verify editorial]
}
Pitfalls
- Each target counts at most once. Multiple F's at the same position give one hit, not many — use a
setor mark targets as used. - The "no change" baseline. You're allowed to change 0 or 1 commands; don't force a change.
- O(C2) is the subtask solution. For full points you must precompute counts of F-positions and shift them by ±1 or ±2 using bucket arrays of size 2C+1.
- Target positions can be negative. Offset by C when indexing into arrays.
What Silver December 2023 was actually testing
- P1 — sweep + greedy with grouped counts. M ≤ 109 tells you the algorithm must be in N, not M.
- P2 — cyclic group action on a sequence. Two cycles' agreement is a histogram over the 2K rotations × reflections.
- P3 — local edits to a simulated trajectory. Change at index k shifts all later positions by a fixed amount — bucket the Fs.