USACO 2020 January · Bronze · all three problems
Problem 1 — Word Processor
Statement
Bessie types an essay of N words with a line width of K characters. She greedily places each word on the current line if it fits (current line length + word length ≤ K), otherwise she starts a new line. Output the essay with words separated by spaces and newlines between filled lines.
Constraints
- 1 ≤ N ≤ 100, 1 ≤ K ≤ 80.
- Each word is 1–K lowercase letters, so it always fits on its own line.
- Time limit: 2s. Memory: 256 MB.
Key idea
Track the length of the current line. For each incoming word, if (current + word_len) ≤ K, append it with a space; otherwise emit a newline and start a new line with this word. No DP needed — it is the classic greedy line-fill ("first-fit").
Complexity target
O(total characters). N · K ≤ 8000 — trivial.
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;
int cur = 0; // chars on current line, NOT counting trailing space
for (int i = 0; i < N; ++i) {
string w; cin >> w;
int L = (int)w.size();
if (cur == 0) {
cout << w;
cur = L;
} else if (cur + 1 + L <= K) {
// Bessie's rule actually checks cur + L (no separator counted),
// because spaces are added when printing but width counts letters.
// The official statement counts only letters toward K.
cout << ' ' << w;
cur += L; // spaces are free per statement [verify cpid=987]
} else {
cout << '\n' << w;
cur = L;
}
}
cout << '\n';
}
Pitfalls
- Spaces don't count toward K. The statement says the line width measures letters only; only word-length contributes to the budget.
- Greedy is optimal here because the problem fixes the algorithm — it is not asking for the minimum number of lines, it is asking you to replay Bessie's algorithm.
- Print exactly one newline between lines and a trailing newline at the end (judge is whitespace-tolerant but be tidy).
- No empty line at start — guard the very first word.
Problem 2 — Photoshoot
Statement
The original lineup is a permutation a1..N of 1..N. Farmer John recorded the array bi = ai + ai+1 for i = 1..N−1 but lost the permutation. Given b, reconstruct the lexicographically smallest valid a.
Constraints
- 2 ≤ N ≤ 103.
- 1 ≤ bi ≤ 2N − 1, and a guaranteed-valid b is provided.
- Time limit: 2s.
Key idea
Once a1 is fixed, the whole permutation is forced: a2 = b1 − a1, a3 = b2 − a2, … Try each candidate a1 from 1 to N in increasing order, generate the implied a, and check that it is a permutation of 1..N (all values in [1,N] and distinct). Output the first that works.
Complexity target
O(N²) candidates × verification — at most 106 ops.
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;
vector<int> b(N - 1);
for (auto& x : b) cin >> x;
for (int a1 = 1; a1 <= N; ++a1) {
vector<int> a(N);
a[0] = a1;
vector<char> seen(N + 1, 0);
bool ok = (a1 >= 1 && a1 <= N);
if (ok) { seen[a1] = 1; }
for (int i = 1; i < N && ok; ++i) {
a[i] = b[i - 1] - a[i - 1];
if (a[i] < 1 || a[i] > N || seen[a[i]]) { ok = false; break; }
seen[a[i]] = 1;
}
if (ok) {
for (int i = 0; i < N; ++i) cout << a[i] << " \n"[i == N - 1];
return 0;
}
}
}
Pitfalls
- Lex-smallest means try a1 ascending, not search for a "best" candidate after enumerating all.
- Reject early. Bail the moment a value leaves [1, N] or repeats — saves a factor of N on average.
- The problem guarantees a solution exists, so you do not need a "no answer" fallback.
- Indexing. b has length N−1, not N. Off-by-one is the classic Bronze trap here.
Problem 3 — Race
Statement
Bessie runs a race on a 1D track of length K. Her speed starts at 0 and changes by ±1 per second; she must finish at speed 0. She gets a fatigue meter: at every second her speed exceeds a threshold T, fatigue increases. Output, for each query, the minimum total time to finish under the constraints. (The Bronze version asks for simple simulations on a fixed plan.)
Constraints
- 1 ≤ K ≤ 109.
- 1 ≤ N ≤ 100 queries, each with its own (cruise speed, max fatigue) pair.
- Time limit: 2s.
Key idea
Bessie's velocity profile is a triangle / trapezoid: accelerate from 0 up to some peak v, cruise, then decelerate from v back to 0. Distance under that profile is v(v+1) + cruise · v (in the standard "speed s for one full second" model). For each query, binary-search or close-form the peak v that exactly uses the fatigue budget, then compute the cruise length so total distance ≥ K.
Complexity target
O(N · log K) using binary search on the peak speed.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// Distance traveled if Bessie peaks at speed v with f fatigue-burning seconds at speed v.
// Accelerate 1..v (v seconds, distance v(v+1)/2), cruise at v for c seconds,
// decelerate v..1 (v seconds, distance v(v+1)/2). Total = v(v+1) + c*v.
// Fatigue spent at speeds > T: count seconds with speed in (T, v].
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
ll K; int N;
cin >> K >> N;
while (N--) {
ll T, F; cin >> T >> F; // cruise threshold, fatigue budget
// Try peak v: fatigue spent = max(0, v - T)*2 + cruise_seconds * max(0, [v > T])
// ... (problem-specific formula; this is a sketch — verify against statement)
ll lo = 1, hi = (ll)2e9, best = 1;
auto feasible = [&](ll v) -> bool {
// Minimal time to cover K with peak v and zero cruise above T:
// accel + decel distance = v*(v+1). If >= K we're fine without cruising over T.
ll triangle = v * (v + 1);
if (triangle >= K) return true;
// Otherwise cruise. Cruising at v > T burns fatigue every second.
ll need = K - triangle;
ll cruise = (need + v - 1) / v;
ll fatigue = (v > T) ? cruise + 2 * (v - T) : 0;
return fatigue <= F;
};
while (lo <= hi) {
ll m = (lo + hi) / 2;
if (feasible(m)) { best = m; hi = m - 1; } else lo = m + 1;
}
// Total time = 2*best + cruise.
ll triangle = best * (best + 1);
ll cruise = (triangle >= K) ? 0 : (K - triangle + best - 1) / best;
cout << (2 * best + cruise) << "\n";
}
}
Pitfalls
- Verify the exact mechanics. The Bronze "Race" statement defines fatigue and speed transitions precisely; this reference is a sketch — match the official formulae before submitting.
- Bessie must finish at speed 0, so the descending leg is forced. Don't forget the +1 second from decelerating to 0.
- 64-bit everywhere. K up to 109, peak speed × time can blow past 32-bit.
- Ceiling division for the cruise count, not floor.
What Bronze January 2020 was actually testing
- P1 — greedy simulation. Replay the rule exactly as stated; do not optimize.
- P2 — constructive permutation. Fix the first element, everything else cascades; enumerate ascending for lex-min.
- P3 — analytic kinematics. Closed-form distance under triangle/trapezoid speed profile; binary search if needed.