USACO 2015 January · Bronze · all four problems
Problem 1 — Cow Routing
Statement
Air Bovinia owns N airline routes. Each route is an ordered list of cities with a fixed cost; if Bessie uses any portion of a route she pays the full cost. She may board at any city along the route and disembark at any later city along the same route. She wants to travel from city A to city B using exactly one route. Output the minimum cost, or -1 if no single route covers her trip.
Constraints
- 1 ≤ N ≤ 500.
- Each route has 1..500 cities; cities are integers 1..10,000.
- Cost per route is in [1, 1000].
- Time limit: 2s. Memory limit: 256 MB.
Key idea
For each route, find the position of A and the position of B (if both exist) and check that A appears before B. If so, that route is usable for cost = route.cost. Take the minimum across all routes.
Complexity target
O(N · L) where L is max route length — at most 250,000 ops.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
freopen("cowroute.in", "r", stdin);
freopen("cowroute.out", "w", stdout);
int A, B, N; cin >> A >> B >> N;
int best = INT_MAX;
for (int i = 0; i < N; ++i) {
int cost, k; cin >> cost >> k;
vector<int> route(k);
for (int j = 0; j < k; ++j) cin >> route[j];
int posA = -1, posB = -1;
for (int j = 0; j < k; ++j) {
if (route[j] == A) posA = j;
if (route[j] == B && posA != -1 && posB == -1) posB = j;
}
if (posA != -1 && posB != -1 && posA < posB)
best = min(best, cost);
}
cout << (best == INT_MAX ? -1 : best) << "\n";
}
Pitfalls
- Order matters within a route. A must appear strictly before B in the city list.
- Pay the full cost regardless of segment length. Don't try to pro-rate.
- Output -1 when no route works. Don't print INT_MAX.
- File I/O. Bronze 2015 used
cowroute.in/cowroute.out; current USACO uses stdin/stdout. Both work in analysis mode if you read the problem header.
Problem 2 — Cow Routing II
Statement
Same setup as Problem 1, but Bessie may use up to two routes (each at most once). Output the minimum total cost from A to B, or -1 if even two routes cannot get her there.
Constraints
- 1 ≤ N ≤ 500; route length ≤ 500; cost ≤ 1000.
- Time limit: 2s. Memory limit: 256 MB.
Key idea
Enumerate pairs (i, j) of routes and an intermediate city C that lies on both: route i must contain A then C in order, route j must contain C then B in order. With N ≤ 500 and L ≤ 500, the worst case is N² · L ≈ 1.25 × 10⁸ which barely fits; use sets to find common cities efficiently, or just iterate. Also handle the single-route case (reuse Problem 1's check) and take the minimum.
Complexity target
O(N² · L) in the worst case; cleaner: O(N · L · log L) using sets keyed by city.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int N, A, B;
vector<int> cost;
vector<vector<int>> rt;
vector<unordered_map<int,int>> pos; // city -> index in route
int main() {
freopen("cowroute.in","r",stdin); freopen("cowroute.out","w",stdout);
cin >> A >> B >> N;
cost.resize(N); rt.resize(N); pos.resize(N);
for (int i = 0; i < N; ++i) {
int k; cin >> cost[i] >> k;
rt[i].resize(k);
for (int j = 0; j < k; ++j) { cin >> rt[i][j]; pos[i][rt[i][j]] = j; }
}
int best = INT_MAX;
// single route
for (int i = 0; i < N; ++i)
if (pos[i].count(A) && pos[i].count(B) && pos[i][A] < pos[i][B])
best = min(best, cost[i]);
// two routes connected via some intermediate C
for (int i = 0; i < N; ++i) if (pos[i].count(A))
for (int j = 0; j < N; ++j) if (i != j && pos[j].count(B))
for (auto &kv : pos[i])
if (kv.second > pos[i][A] && pos[j].count(kv.first) && pos[j][kv.first] < pos[j][B]) {
best = min(best, cost[i] + cost[j]); break;
}
cout << (best == INT_MAX ? -1 : best) << "\n";
}
Pitfalls
- Don't forget the single-route case. "Up to two" includes one.
- Routes are distinct. i ≠ j; you can't combine a route with itself.
- Direction within each route. Boarding city must come before disembark city.
- Use long long if you extend to costs > 10⁶. For Bronze the costs fit in int, but the Silver variant explicitly warns about 64-bit.
Problem 3 — It's All About the Base
Statement
Bessie wrote the same integer N in two different bases X and Y (both in [10, 15000]); in each base the result was a 3-digit number with each digit in [1, 9]. Given the two 3-digit sequences (as base-10 decimal numbers from the input — e.g. "419" meaning digits 4, 1, 9), recover X and Y. A unique solution is guaranteed. K test cases.
Constraints
- K ≥ 1 test cases.
- X, Y in [10, 15000]; each digit of the 3-digit numbers in [1, 9].
- Time limit: 2s — naive O(15000²) per test is too slow.
Key idea
A 3-digit number with digits (a, b, c) in base X equals a·X² + b·X + c, a quadratic in X. For each candidate X in [10, 15000] compute N = a·X² + b·X + c and store in a hash map N → X. Then iterate candidate Y in [10, 15000], compute M = a'·Y² + b'·Y + c', and look it up in the map. The unique pair (X, Y) with the same N is the answer. O(R) per test (R = 15000).
Complexity target
O(K · R) with hash map; R ≤ 15000.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
freopen("whatbase.in","r",stdin); freopen("whatbase.out","w",stdout);
int K; cin >> K;
while (K--) {
string sa, sb; cin >> sa >> sb;
int a1 = sa[0]-'0', a2 = sa[1]-'0', a3 = sa[2]-'0';
int b1 = sb[0]-'0', b2 = sb[1]-'0', b3 = sb[2]-'0';
unordered_map<long long,int> seen;
for (int X = 10; X <= 15000; ++X) {
long long N = 1LL*a1*X*X + 1LL*a2*X + a3;
seen[N] = X;
}
int ansX = -1, ansY = -1;
for (int Y = 10; Y <= 15000 && ansY == -1; ++Y) {
long long M = 1LL*b1*Y*Y + 1LL*b2*Y + b3;
auto it = seen.find(M);
if (it != seen.end() && it->second != Y) { ansX = it->second; ansY = Y; }
}
cout << ansX << " " << ansY << "\n";
}
}
Pitfalls
- X ≠ Y. A 3-digit pattern in the same base is trivially equal — the problem wants two distinct bases.
- Use 64-bit. Max N ≈ 9 · 15000² ≈ 2 · 10⁹, overflows 32-bit signed.
- Output order is X then Y. Match the order of the two input numbers.
- Don't brute 15000² per test. Hash lookup is essential.
Problem 4 — Meeting Time
Statement
A DAG of N fields (numbered 1..N with edges only from low index to high). M directed downhill paths,
each carrying two weights: Bessie's traversal time C and Elsie's traversal time D. Bessie and Elsie
leave field 1 at the same moment and want to both arrive at field N at the same moment. Output the
smallest such common arrival time, or IMPOSSIBLE if no pair of (possibly different) paths
of equal total length exists.
Constraints
- 1 ≤ N ≤ 16; M ≤ N(N-1)/2.
- C, D ∈ [1, 1000].
- Time limit: 2s. Memory limit: 256 MB.
Key idea
For each vertex v keep two bit-sets: reachB[v] = set of total Bessie-times for any path from 1 to v, reachE[v] = similar for Elsie. Both sets grow by topological-order DP. Path totals are at most 15 · 1000 = 15000, so a bitset of length 15001 suffices. Answer = smallest t in reachB[N] ∩ reachE[N].
Complexity target
O(M · T / 64) with T = 15001 — instant.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
const int T = 16005;
int main() {
freopen("meeting.in","r",stdin); freopen("meeting.out","w",stdout);
int N, M; cin >> N >> M;
vector<tuple<int,int,int>> outB[17], outE[17]; // (to, weight) per source
vector<bitset<T>> B(N+1), E(N+1);
B[1].set(0); E[1].set(0);
vector<vector<tuple<int,int,int>>> adj(N+1); // to, c, d
for (int i = 0; i < M; ++i) {
int a, b, c, d; cin >> a >> b >> c >> d; // a < b
adj[a].push_back({b, c, d});
}
for (int u = 1; u <= N; ++u)
for (auto [v, c, d] : adj[u]) {
B[v] |= (B[u] << c);
E[v] |= (E[u] << d);
}
bitset<T> both = B[N] & E[N];
int ans = -1;
for (int t = 1; t < T; ++t) if (both[t]) { ans = t; break; }
if (ans == -1) cout << "IMPOSSIBLE\n"; else cout << ans << "\n";
}
Pitfalls
- t = 0 is not a valid answer. They leave at time 0 but must arrive at field N (not stay at field 1). Skip bit 0.
- Unreachable field N. If B[N].none() or E[N].none(), output IMPOSSIBLE.
- Edge direction. Input gives A < B; the path goes A → B.
- Bitset size matters. Max path total is N · maxWeight = 15 · 1000 = 15000; allocate ≥ 15001 bits.
What Bronze January 2015 was actually testing
- P1 — straight simulation of the route structure with order-in-list checks.
- P2 — combine two structures (any pair of routes) with a meeting city, foreshadowing the Silver Dijkstra variant.
- P3 — hash-map meet-in-the-middle on a quadratic equation; the brute-force ban was explicit.
- P4 — DP over reachable time sets on a tiny DAG; bitset is the cleanest representation.