USACO 2015 US Open · Silver · all three problems
Problem 1 — Bessie Goes Moo
Statement
Given lists of integer values for variables B, E, S, I, G, O, M (each with up to 500 listed values),
count the number of distinct assignments such that the product
(B+E+S+S+I+E) · (G+O+E+S) · (M+O+O) is divisible by 7.
Constraints
- 1 ≤ N ≤ 3500 (≤ 500 values per variable).
- Values in [−105, 105].
- Answer fits in 64-bit.
- Time limit: 2s.
Key idea
Reduce every value mod 7. The product is 0 mod 7 iff at least one factor is. Enumerate residues: each of the 7 variables has 7 residue buckets, giving 77 ≈ 823 543 tuples — small. For each tuple compute the three factor residues mod 7 and check if any is 0. Multiply the per-variable bucket counts and add to the total. Naively 77 < 106; fast.
Smarter (and how the official editorial usually frames it): split into halves of three or four variables, build a residue distribution for each factor, then combine via a 7-bucket convolution. For 2015 Silver the 77 brute force is well within limits.
Complexity target
O(77) ≈ 8·105 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 N; cin >> N;
map<char,int> id = {{'B',0},{'E',1},{'S',2},{'I',3},{'G',4},{'O',5},{'M',6}};
ll cnt[7][7] = {{0}}; // cnt[var][residue mod 7]
for (int i = 0; i < N; ++i) {
char v; int x; cin >> v >> x;
cnt[id[v]][((x % 7) + 7) % 7]++;
}
ll ans = 0;
int r[7];
// Enumerate residues of (B, E, S, I, G, O, M) = indices 0..6.
for (r[0] = 0; r[0] < 7; ++r[0])
for (r[1] = 0; r[1] < 7; ++r[1])
for (r[2] = 0; r[2] < 7; ++r[2])
for (r[3] = 0; r[3] < 7; ++r[3])
for (r[4] = 0; r[4] < 7; ++r[4])
for (r[5] = 0; r[5] < 7; ++r[5])
for (r[6] = 0; r[6] < 7; ++r[6]) {
int F1 = (r[0] + r[1] + r[2] + r[2] + r[3] + r[1]) % 7; // B+E+S+S+I+E
int F2 = (r[4] + r[5] + r[1] + r[2]) % 7; // G+O+E+S
int F3 = (r[6] + r[5] + r[5]) % 7; // M+O+O
if (F1 != 0 && F2 != 0 && F3 != 0) continue;
ll ways = 1;
for (int i = 0; i < 7; ++i) ways *= cnt[i][r[i]];
ans += ways;
}
cout << ans << "\n";
}
Pitfalls
- Negative mod.
((x % 7) + 7) % 7. - Don't multiply raw values. Sums can exceed 32-bit during inner loops in other formulations; here we work entirely in residues so the multiplications are over counts, not values.
- "Different assignments" counts each chosen value separately. If a variable has duplicate listed values (problem says no duplicates per variable), each value index is distinct.
- Answer is 64-bit. Up to 5007 ≈ 7.8·1018 in theory; will overflow unsigned 32 — use
long long.
Problem 2 — Trapped in the Haybales (Silver)
Statement
Same setup as Bronze, but now Bessie's position B is fixed and she is currently not trapped. FJ wants to add hay to exactly one bale (any single bale; you choose) to trap her. Output the minimum total hay he must add — or −1 if no single-bale increment suffices.
Constraints
- 2 ≤ N ≤ 105.
- 1 ≤ pi, si ≤ 109; B is an integer not at any bale.
- Time limit: 2s.
Key idea
Simulate escape on each side independently. To trap Bessie you must block both sides. So you only need to enlarge one bale on one side, plus a separate one on the other? No — exactly one bale total. That means at least one side must already trap her without help; otherwise no single addition works (output −1). So compute: does she currently escape left? right? If both, return −1. Suppose she currently escapes (say) right; she's already trapped left (else she'd escape there). Now binary search the additional size Δ added to some bale on the right side to stop her.
Concretely: for the side where she escapes, simulate her escape and try, for each bale on that side, the minimum Δ that flips the predicate. The minimum across bales is the answer. With sorted bales and N up to 105, a binary search on Δ per candidate bale, with O(N) simulation, is O(N² log V) worst case — fine for N=105? Actually N² is 1010; we need smarter. The standard Silver-difficulty approach: binary-search the single value Δ added to the bale that maximally stops her — i.e., binary search on the answer Δ globally, and per check, simulate escape with Δ added to whichever bale she would currently break (the cheapest spot to upgrade) — total O(N log V).
Complexity target
O(N log V), V = max coordinate (109).
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int N;
ll B;
vector<pair<ll,ll>> bale; // sorted by position: (pos, size)
// Can Bessie escape to the right when one bale (index addIdx, -1 = none) has +delta size?
bool escapesRight(int addIdx, ll delta) {
// Find first bale strictly right of B.
int i = upper_bound(bale.begin(), bale.end(), make_pair(B, LLONG_MAX)) - bale.begin();
ll pos = B;
while (i < N) {
ll sz = bale[i].second + (i == addIdx ? delta : 0);
ll D = bale[i].first - pos;
if (D > sz) { pos = bale[i].first; i++; } else return false;
}
return true;
}
bool escapesLeft(int addIdx, ll delta) {
int i = (int)(lower_bound(bale.begin(), bale.end(), make_pair(B, 0LL)) - bale.begin()) - 1;
ll pos = B;
while (i >= 0) {
ll sz = bale[i].second + (i == addIdx ? delta : 0);
ll D = pos - bale[i].first;
if (D > sz) { pos = bale[i].first; i--; } else return false;
}
return true;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N >> B;
bale.resize(N);
for (auto& p : bale) cin >> p.second >> p.first;
sort(bale.begin(), bale.end());
bool eR = escapesRight(-1, 0);
bool eL = escapesLeft(-1, 0);
// To trap with a single bale enlargement, one side must already block her.
if (eR && eL) { cout << -1 << "\n"; return 0; }
if (!eR && !eL) { cout << 0 << "\n"; return 0; } // already trapped
// She currently escapes exactly one side; pick the right helper.
bool toRight = eR;
ll best = LLONG_MAX;
// For each bale on that side, binary search the min delta to stop her.
int lo, hi;
if (toRight) {
lo = upper_bound(bale.begin(), bale.end(), make_pair(B, LLONG_MAX)) - bale.begin();
hi = N - 1;
} else {
lo = 0;
hi = (int)(lower_bound(bale.begin(), bale.end(), make_pair(B, 0LL)) - bale.begin()) - 1;
}
for (int idx = lo; idx <= hi; ++idx) {
ll l = 0, r = (ll)2e9, ans = -1;
while (l <= r) {
ll m = l + (r - l) / 2;
bool esc = toRight ? escapesRight(idx, m) : escapesLeft(idx, m);
if (!esc) { ans = m; r = m - 1; } else l = m + 1;
}
if (ans != -1) best = min(best, ans);
}
cout << (best == LLONG_MAX ? -1 : best) << "\n";
}
Pitfalls
- Per-bale binary search is O(N² log V). For Silver N ≤ 105, this borderlines TLE; the cleaner approach is to recognize the optimal bale is the one with the smallest "needed bump" — derivable in one sweep. The reference above is a clear-but-slower version; for safety prune by giving up early once
bestcan't improve. - Strictly greater than. Bessie breaks bale of size s iff her run distance D > s — strict.
- −1 cases. If she escapes both sides, no single bale addition can fix both.
- Long long throughout. Positions and sizes up to 109; sums can hit 1010.
Problem 3 — Bessie's Birthday Buffet
Statement
A row of N grass patches has qualities q1..qN (all distinct). Bessie can start at any patch and move between adjacent patches at a cost of E energy per step. She may eat any patch she visits, gaining its quality in energy — but once she eats quality v, she may never again eat a patch of quality ≤ v. (She can walk through any patch without eating.) Maximize her total energy.
Constraints
- 1 ≤ N ≤ 1000.
- 1 ≤ qi ≤ 106, all distinct; 1 ≤ E ≤ 106.
- Time limit: 2s.
Key idea
Sort patches by quality ascending; the sequence of eaten patches must be a subsequence of this
sorted order. DP: dp[i] = max energy when the last patch eaten is sorted-rank i.
Transition: dp[i] = q[i] + max(0, max over j < i of dp[j] − E · |pos[i] − pos[j]|).
Answer is max dp[i]. N = 1000 makes the O(N²) DP trivially fast.
Complexity target
O(N²) ≈ 106.
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 E; cin >> N >> E;
vector<pair<ll,int>> a(N); // (quality, original index)
for (int i = 0; i < N; ++i) { cin >> a[i].first; a[i].second = i; }
sort(a.begin(), a.end()); // ascending by quality
vector<ll> dp(N, 0);
ll ans = 0;
for (int i = 0; i < N; ++i) {
ll best = 0; // option: start here (eat only i)
for (int j = 0; j < i; ++j) {
ll travel = E * (ll)abs(a[i].second - a[j].second);
ll cand = dp[j] - travel;
if (cand > best) best = cand;
}
dp[i] = a[i].first + best;
ans = max(ans, dp[i]);
}
cout << ans << "\n";
}
Pitfalls
- Strictly increasing quality. "Never eats grass at or below quality" means each next eaten patch is strictly higher. Sorting + sequencing handles it.
- Travel cost can exceed prior dp value. Cap the transition at
max(0, ...)so she may also start fresh at patch i. - Distance is in patches, not positions. Step cost E per adjacent patch traversed.
- Long long. N · q ≈ 109; E · N ≈ 109; intermediates need 64 bits.
- Don't forget: she can start anywhere. Initialize each
dp[i]'s "best" baseline to 0 (i.e., starting fresh at i).
What Silver 2015 US Open was actually testing
- P1 — bucketed enumeration. 77 residue tuples is the polite version of "convolve three independent factor distributions mod 7."
- P2 — predicate-search + careful simulation. The escape predicate is monotone in added hay; binary search.
- P3 — sort-by-key DP. Sequencing constraint becomes "subsequence in sorted order"; classic O(N²) DP with a cost transition.