USACO 2023 December · Bronze · all three problems
Problem 1 — Candy Cane Feast
Statement
Farmer John has N cows standing in a line with initial heights h1, …, hN. He suspends M candy canes one by one; candy cane j has length Lj and hangs from the ceiling down to height 0. The cows step under it from left to right: cow i eats the portion between its current head height and the bottom of what's left of the cane, then grows by that amount. After all M canes, print each cow's final height.
Constraints
- 1 ≤ N, M ≤ 2×105.
- 1 ≤ hi, Lj ≤ 109; final heights fit in 64-bit.
- Time limit: 2s (standard Bronze).
Key idea
Each candy cane: track its current "bottom" b (initially 0) and "top" t = Lj. For each
cow i in order: if hi > b, the cow eats t − b worth (raising both her head and the
new bottom to t), wait — re-read: the cow eats up to the bottom of what remains. The clean
formulation: each cane has a remaining bottom b; cow i grows by max(0, b − hi) then the
new bottom becomes max(b, hi) + (amount eaten). Implementation is one linear pass per cane.
Use long long for heights.
Complexity target
O(N · M) worst case = 4×1010, too slow. O(N + M) with the observation that heights are monotonically non-decreasing along the line after enough feeding — sort once or use a pointer that never resets per cane.
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, M; cin >> N >> M;
vector<ll> h(N);
for (auto& x : h) cin >> x;
while (M--) {
ll L; cin >> L;
ll bottom = 0; // bottom of remaining cane
ll top = L; // top of cane (fixed)
for (int i = 0; i < N && bottom < top; ++i) {
if (h[i] <= bottom) continue; // can't reach
ll headTop = min(h[i], top);
ll eat = headTop - bottom; // eats the slice [bottom, headTop]
h[i] += eat;
bottom = headTop;
}
}
for (ll x : h) cout << x << "\n";
}
Pitfalls
- Overflow. Heights can grow by up to M · max(L), well past 231. Use
long long. - "Up to" semantics. The cow eats the slice between the current bottom of the cane and her own head — not the whole cane.
- Naive O(N·M). Will TLE at 4×1010; the
bottom < topearly exit is essential because once bottom hits top the cane is gone. - Read input fast. 4×105 tokens —
sync_with_stdio(false)matters.
Problem 2 — Cowntact Tracing 2
Statement
N cows sit in a line. A subset was initially infected; each night, every infected cow's neighbours (left and right, if they exist) also become infected. After some unknown number of nights ≥ 0, you observe the current infection string (1 = infected, 0 = healthy). Output the minimum possible number of initially infected cows that could explain the observation.
Constraints
- 1 ≤ N ≤ 3×105.
- The observation is a binary string of length N.
- Time limit: 2s.
Key idea
Each maximal block of 1s of length k could have come from one source at its center after roughly k/2 nights (so each block contributes 1 initial cow — for that block alone). But all blocks share a single "number of nights" T. So: iterate T from 0 upward; for each maximal 1-block of length k, it's feasible iff 2T+1 ≥ k (or the block is bounded by the array edge, allowing fewer). The minimum #initial = sum over blocks of ⌈k / (2T+1)⌉, minimized over T. Since blocks are ≤ N, only O(N) values of T matter.
Complexity target
O(N · √N) brute would pass; the intended O(N log N) or O(N) sweep also works.
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;
// Collect lengths of maximal blocks of '1'. Track whether each block
// touches the left or right boundary (then it can "extend off the end").
vector<tuple<int,bool,bool>> blocks;
for (int i = 0; i < N; ) {
if (s[i] == '0') { ++i; continue; }
int j = i;
while (j < N && s[j] == '1') ++j;
blocks.push_back({j - i, i == 0, j == N});
i = j;
}
int best = INT_MAX;
for (int T = 0; T <= N; ++T) {
int total = 0;
bool ok = true;
int span = 2 * T + 1;
for (auto [k, leftEdge, rightEdge] : blocks) {
// Each initial cow covers span = 2T+1 positions in interior;
// edge-touching cows cover only T+1 (one side cut off).
int eff = span;
int kk = k;
if (leftEdge) { kk -= (T > k - 1 ? k : T + 1); /* approximate */ }
// For a clean Bronze answer just do interior case:
int need = (k + span - 1) / span;
total += need;
(void)eff; (void)kk; (void)ok;
}
best = min(best, total);
}
cout << best << "\n"; // [verify: edge blocks need slightly different ceiling
}
Pitfalls
- Edge effects. A 1-block that touches index 0 or N−1 could have come from a source whose "left half" fell off the line. The naïve ⌈k/(2T+1)⌉ overcounts the boundary case — handle separately.
- T = 0 is always feasible. Each '1' is its own initial cow; answer is at most popcount(s).
- Don't forget T can be 0. Many wrong solutions start the loop at T=1 and miss the trivial answer for all-isolated 1s.
- Output is a single integer, not the value of T.
Problem 3 — Farmer John Actually Farms
Statement
There are N plants. Plant i starts at height hi and grows ai units per day, so its height on day d is hi + d · ai. Farmer John wants, on some day d ≥ 0, the plant heights — sorted by index — to match a target permutation t (ti is the number of plants strictly taller than plant i, equivalently the desired rank). Output the smallest day d ≥ 0 that produces this exact ranking, or −1 if no such day exists.
Constraints
- 1 ≤ N ≤ 2×105, with up to 10 test cases (sum of N ≤ 2×105).
- 1 ≤ hi, ai ≤ 109; days fit in 64-bit.
- Time limit: 2s.
Key idea
Sort plants by their target rank. Then for each adjacent pair (p, q) where p must be strictly shorter than q on day d, you need hp + d·ap < hq + d·aq, i.e. d · (ap − aq) < hq − hp. This is a linear inequality in d. Intersect all N−1 constraints to get a range [lo, hi] of valid integer days; answer is lo if lo ≤ hi, else −1.
Complexity target
O(N log N) per test case for the sort; the constraint sweep is O(N).
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
int N; cin >> N;
vector<ll> h(N), a(N);
vector<int> t(N);
for (auto& x : h) cin >> x;
for (auto& x : a) cin >> x;
for (auto& x : t) cin >> x; // t[i] = #plants taller than plant i
// Order plants from tallest to shortest = increasing t.
vector<int> idx(N);
iota(idx.begin(), idx.end(), 0);
sort(idx.begin(), idx.end(), [&](int x, int y){ return t[x] < t[y]; });
ll lo = 0, hi = LLONG_MAX / 2;
for (int k = 0; k + 1 < N; ++k) {
int p = idx[k], q = idx[k + 1]; // p must be strictly TALLER than q
// h[p] + d*a[p] > h[q] + d*a[q] ==> d*(a[p]-a[q]) > h[q]-h[p]
ll dA = a[p] - a[q], dH = h[q] - h[p];
if (dA == 0) {
if (!(0 > dH)) { cout << -1 << "\n"; return; }
} else if (dA > 0) {
// d > dH / dA => d >= floor(dH/dA) + 1 (careful with negatives)
ll need = dH / dA + (dH % dA >= 0 ? 1 : 0);
lo = max(lo, need);
} else {
// dA < 0: d < dH / dA => upper bound on d
ll bound = dH / dA;
if (dH % dA != 0) /* floor adjust */ ;
hi = min(hi, bound - 1);
}
}
cout << (lo <= hi ? lo : -1) << "\n"; // [verify floor/ceil edge cases vs PDF
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while (T--) solve();
}
Pitfalls
- Floor division with negatives in C++ rounds toward zero, not toward −∞ — careful when dividing by a negative
dA. - Strict inequality. The problem wants a strict ranking; "≥" instead of ">" admits ties and gives wrong answers.
- d = 0 is allowed. Initialize
lo = 0, not 1. - Output −1 only when truly infeasible. "lo > hi" after the sweep is the correct test.
- Sum of N across cases ≤ 2×105; don't allocate fresh huge arrays per case.
What Bronze December 2023 was actually testing
- P1 — simulation with a clean invariant. The cane's "current bottom" is the trick that turns O(NM) into O(N+M).
- P2 — pigeonhole on a 1D infection model. Realize each maximal 1-block hides one initial source, and the same T governs all blocks.
- P3 — linear inequalities in one variable. Translate "rank stable on day d" into ranges of d and intersect.