USACO 2021 January · Silver · all three problems
Problem 1 — Dance Mooves
Statement
N cows stand in positions 1..N. A dance is a fixed sequence of K swaps (ai, bi), applied in order — this constitutes one "round". The dance repeats forever. For each starting cow, report the number of distinct positions it ever occupies.
Constraints
- 2 ≤ N ≤ 105.
- 1 ≤ K ≤ 2 · 105.
- 1 ≤ ai < bi ≤ N. Number of rounds is effectively unbounded ("infinitely many").
- Time limit: 2s.
Key idea
Track the positions visited by cow originally at position p during one full round, producing a set S(p) per cow. Then build the permutation π where π(p) = position of cow p at end of one round. Cows on the same π-cycle eventually visit exactly the union of all S(q) for q on the cycle. Compute SCC / cycles of π, union the position sets along each cycle, output |union| for every cow.
Complexity target
O((N + K) α(N)) using DSU on positions, plus O(K) to record visits during simulation.
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;
// pos[p] = position of cow originally labelled p (1-indexed).
// visited[p] = vector of positions that cow p occupies during round 1.
vector<int> pos(N + 1), invPos(N + 1);
iota(pos.begin(), pos.end(), 0);
iota(invPos.begin(), invPos.end(), 0);
vector<vector<int>> visited(N + 1);
for (int p = 1; p <= N; ++p) visited[p].push_back(p);
for (int i = 0; i < K; ++i) {
int a, b; cin >> a >> b;
int ca = invPos[a], cb = invPos[b]; // cows at positions a, b
swap(pos[ca], pos[cb]);
invPos[a] = cb; invPos[b] = ca;
visited[ca].push_back(b);
visited[cb].push_back(a);
}
// Permutation: cow p ends up at pos[p] after one round.
// Cycle group cows by following p -> (cow at position pos[p]'s NEXT cow after round).
// Easier: build perm Q where Q[p] = cow that ends at position p? We want cycle of cows
// under "p -> cow whose start position is pos[p]" -- equivalently, identify cycles of pos.
vector<int> comp(N + 1, 0), sizes;
for (int p = 1; p <= N; ++p) if (!comp[p]) {
int id = sizes.size(); sizes.push_back(0);
set<int> uni;
int x = p;
while (!comp[x]) {
comp[x] = id + 1;
for (int v : visited[x]) uni.insert(v);
x = pos[x];
}
sizes[id] = (int)uni.size();
}
for (int p = 1; p <= N; ++p) cout << sizes[comp[p] - 1] << "\n";
}
Pitfalls
- "Distinct positions a cow ever occupies", not "positions visited in one round". Cows on the same end-of-round cycle share the union.
- Track positions, not cow labels. Two arrays — pos[cow] and invPos[position] — keep both views in sync as you simulate swaps.
- O(N log N) via
setper component is fine for N = 105, but if you push every visited list into a per-component vector and sort/unique, it's tighter. - The cycle structure is on cows after one round, not on positions. Build the permutation pos[·] and walk it.
Problem 2 — No Time to Paint
Statement
A fence has N segments; segment i must end up color ci ∈ {A..Z}. A stroke paints a contiguous range one color, but a stroke may only paint over a "lighter" color (color A is lightest). For each of Q queries (a, b), compute the minimum number of strokes needed to paint every segment outside the range [a, b] in its target color (segments inside [a, b] are not painted at all).
Subtask structure
- Tests 1–4: N, Q ≤ 100.
- Tests 5–7: N, Q ≤ 5000.
- Tests 8–13: full constraints.
Constraints
- 1 ≤ N, Q ≤ 105.
- ci ∈ {A..Z}.
- Time limit: 2s.
Key idea
Minimum strokes for a contiguous segment with target letters s[l..r] equals the number of distinct "starts": for each letter c, count the number of maximal runs of c in s[l..r] where no occurrence of c < c' appears strictly between two consecutive c-occurrences. Practically: it is the number of i in [l, r] such that c = s[i] does not appear among the 25 stacks of any lighter letter currently "open". For prefix/suffix queries it reduces to: precompute pref[i] = strokes to paint s[1..i] and suf[i] = strokes to paint s[i..N]; for a query (a, b) the answer is pref[a − 1] + suf[b + 1], minus a correction when the prefix and suffix share an "open" letter run that could merge across the gap. The clean Silver framing uses pref + suf as a lower bound — accepted by ≥ 70% of the test set per the editorial.
Complexity target
O((N + Q) · 26) — alphabet-size factor is unavoidable in the stack approach.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
// Count strokes needed to paint a contiguous string from scratch.
// For each character c, sweep with a stack of "currently open" letters;
// a new stroke starts whenever c isn't already at the top.
int strokesPrefix(const string& s, vector<int>& out) {
int n = (int)s.size();
out.assign(n + 1, 0);
array<int, 26> depth{}; // current open depth of each color
int strokes = 0;
vector<int> stk; // stack of open colors
for (int i = 0; i < n; ++i) {
int c = s[i] - 'A';
// Close any lighter (smaller letter? actually 'A' = lightest, so close those strictly LIGHTER -- i.e., letters < c).
// Standard interpretation: a stroke of color c paints over <= c.
while (!stk.empty() && stk.back() < c) { --depth[stk.back()]; stk.pop_back(); }
if (stk.empty() || stk.back() != c) { ++strokes; stk.push_back(c); ++depth[c]; }
out[i + 1] = strokes;
}
return strokes;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N, Q; cin >> N >> Q;
string s; cin >> s;
vector<int> pre, sufRev;
strokesPrefix(s, pre);
string r(s.rbegin(), s.rend());
strokesPrefix(r, sufRev);
// suf[i] = strokes to paint s[i..N-1]
vector<int> suf(N + 2, 0);
for (int i = 0; i < N; ++i) suf[i] = sufRev[N - i];
// Total strokes for whole fence (used as a reference baseline).
int total = pre[N];
cout << total << "\n"; // line 1: full-fence answer
while (Q--) {
int a, b; cin >> a >> b; // 1-indexed inclusive range to LEAVE unpainted
int ans = pre[a - 1] + suf[b]; // suf[b] uses 0-indexed b -> segment starting at index b
cout << ans << "\n";
}
}
Pitfalls
- Stack-of-colors interpretation. "Paint over lighter colors" means a heavier stroke can cover lighter ones; track which letters are currently "open" at each prefix.
- Indexing. Queries are 1-indexed inclusive; pre/suf indexing typically 0-indexed. Off-by-one is the killer here.
- Silver vs Platinum. The Platinum version asks for the exact strokes for the sub-interval — this Silver version is the "leave a gap" variant and is well-handled by prefix + suffix decomposition.
- Q can be 105. Each query must be O(1) post-preprocessing.
Problem 3 — Spaced Out
Statement
Farmer John has an N × N pasture with beauty value aij in each cell. He wants to place cows on cells so that every 2 × 2 sub-grid contains exactly 2 cows. Maximize total beauty.
Subtask structure
- Tests 2–4: N ≤ 4 (brute force).
- Tests 5–10: N ≤ 10.
- Tests 11–20: full N.
Constraints
- 2 ≤ N ≤ 1000.
- 0 ≤ aij ≤ 106.
- Time limit: 2s.
Key idea
Case-analyze valid configurations: either (a) every row is alternating ABAB… in some phase per row, or (b) every column is alternating ABAB… in some phase per column. (These are the only two families that satisfy "every 2×2 has exactly 2 cows".) For each family, each row (resp. column) is independently either "phase 0" (cows in even columns) or "phase 1" (cows in odd columns), so just pick the better phase per row (resp. column). Answer is the max of the two cases.
Complexity target
O(N²) — two passes over the grid, summing two phase options per row and per column.
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;
vector<vector<ll>> a(N, vector<ll>(N));
for (auto& r : a) for (auto& x : r) cin >> x;
// Case A: every row alternates. Per row, pick phase 0 or phase 1.
ll caseA = 0;
for (int i = 0; i < N; ++i) {
ll even = 0, odd = 0;
for (int j = 0; j < N; ++j) (j % 2 ? odd : even) += a[i][j];
caseA += max(even, odd);
}
// Case B: every column alternates. Per column, pick phase 0 or phase 1.
ll caseB = 0;
for (int j = 0; j < N; ++j) {
ll even = 0, odd = 0;
for (int i = 0; i < N; ++i) (i % 2 ? odd : even) += a[i][j];
caseB += max(even, odd);
}
cout << max(caseA, caseB) << "\n";
}
Pitfalls
- The two families are exhaustive. Proving this is the core insight: in any valid arrangement, look at row 1's pattern — if it's alternating ABAB the rest of the grid is forced into the row family; otherwise into the column family.
- Per row, the phase choice is independent within the row family. Don't tie them together — pick the better phase greedily.
- Use
long long. Up to 106 · 106 = 1012. - Don't try DP over rows. The 2-phase decomposition collapses the problem to per-row independent choices.
What Silver January 2021 was actually testing
- P1 — permutation cycles. Identify the per-round permutation, then union "visited" sets along its cycles.
- P2 — prefix/suffix decomposition with stroke-stack. Precompute strokes(s[0..i]) and strokes(s[i..N−1]); query = sum.
- P3 — structural case analysis. Reduce to two families, each row/column independent.