USACO 2020 February · Silver · all three problems
Problem 1 — Swapity Swapity Swap
Statement
The Silver upgrade of the Bronze shuffle. N cows in a line, M fixed range-reversal operations form one "round"; apply K rounds and output the final line. (Bronze had M = 2 and N = 100; Silver scales both.)
Constraints
- 1 ≤ N ≤ 105.
- 1 ≤ M ≤ 100.
- 1 ≤ K ≤ 109.
- Time limit: 2s.
Key idea
Same as Bronze: build the permutation π for one round in O(N · M) by applying the M reversals to the identity, then cycle-decompose. Within each cycle of length L, every element advances by K mod L steps. Total work O(N · M + N).
Complexity target
O(N · M) to build π, O(N) to decompose. 107 ops — fast.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N, M; long long K;
cin >> N >> M >> K;
vector<int> pos(N);
iota(pos.begin(), pos.end(), 0);
for (int m = 0; m < M; ++m) {
int a, b; cin >> a >> b; --a; --b;
reverse(pos.begin() + a, pos.begin() + b + 1);
}
// pos[i] = original cow index that ends up at position i after one round.
vector<int> ans(N);
vector<char> seen(N, 0);
for (int i = 0; i < N; ++i) if (!seen[i]) {
vector<int> cyc;
int x = i;
while (!seen[x]) { seen[x] = 1; cyc.push_back(x); x = pos[x]; }
int L = cyc.size();
long long s = K % L;
for (int j = 0; j < L; ++j)
ans[cyc[j]] = cyc[(j + s) % L] + 1;
}
for (int v : ans) cout << v << "\n";
}
Pitfalls
- Direction of iteration in the cycle. If you wrote "pos[i] = where cow i ends up" instead of "which cow lands at i", swap the cycle direction.
- K can be 109. Cycle-mod is essential.
- Memory. Two int vectors of size N is fine; don't allocate per-cycle scratch larger than needed.
- Don't apply the M reversals K times naively. O(N · M · K) = up to 1016.
Problem 2 — Triangles
Statement
Silver version of Bronze Triangles. Given N fence posts at integer coordinates, compute the sum of (2·area) over every right triangle with axis-aligned legs whose three vertices are posts. Output modulo 109 + 7.
Constraints
- 1 ≤ N ≤ 105.
- Coordinates fit in [−104, 104].
- Answer mod 109 + 7.
- Time limit: 2s.
Key idea
Fix corner C. Its contribution is (sum of |Δx| over same-y posts) · (sum of |Δy| over same-x posts). Group posts by y-coordinate and by x-coordinate. For each y-group, sort by x and precompute, for every post, the sum of |x − xi| over the group (prefix-sum trick: each x contributes (k·x − prefixSum) on the left and (suffixSum − (m−k)·x) on the right). Same for x-groups. Multiply and sum.
Complexity target
O(N log N) — dominated by sorting within groups.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 1'000'000'007;
// For a sorted vector v, return, for each i, the sum of |v[i] - v[j]| over j.
vector<ll> absSumPerIndex(vector<ll> v) {
int n = v.size();
sort(v.begin(), v.end());
vector<ll> pref(n + 1, 0);
for (int i = 0; i < n; ++i) pref[i + 1] = pref[i] + v[i];
vector<ll> out(n);
for (int i = 0; i < n; ++i)
out[i] = (ll)i * v[i] - pref[i] + (pref[n] - pref[i + 1]) - (ll)(n - 1 - i) * v[i];
return out;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N; cin >> N;
vector<ll> X(N), Y(N);
for (int i = 0; i < N; ++i) cin >> X[i] >> Y[i];
// For each post, sumDx[i] = sum |X[i]-X[j]| over j sharing Y; sumDy similarly.
map<ll, vector<int>> byY, byX;
for (int i = 0; i < N; ++i) { byY[Y[i]].push_back(i); byX[X[i]].push_back(i); }
vector<ll> sumDx(N, 0), sumDy(N, 0);
for (auto& [y, idx] : byY) {
vector<ll> xs; for (int i : idx) xs.push_back(X[i]);
// We need result aligned to idx order — sort idx by X too.
vector<int> ord = idx;
sort(ord.begin(), ord.end(), [&](int a, int b){ return X[a] < X[b]; });
vector<ll> xs2; for (int i : ord) xs2.push_back(X[i]);
auto res = absSumPerIndex(xs2);
for (int k = 0; k < (int)ord.size(); ++k) sumDx[ord[k]] = res[k];
}
for (auto& [x, idx] : byX) {
vector<int> ord = idx;
sort(ord.begin(), ord.end(), [&](int a, int b){ return Y[a] < Y[b]; });
vector<ll> ys2; for (int i : ord) ys2.push_back(Y[i]);
auto res = absSumPerIndex(ys2);
for (int k = 0; k < (int)ord.size(); ++k) sumDy[ord[k]] = res[k];
}
ll ans = 0;
for (int i = 0; i < N; ++i)
ans = (ans + sumDx[i] % MOD * (sumDy[i] % MOD)) % MOD;
cout << ans << "\n";
}
Pitfalls
- Modulo only at the very end (or after each multiply). Coordinates fit in 32-bit but products are up to ~1013 — use 64-bit before mod.
- Negative coordinates are fine after sorting — we sum |Δ|, not signed Δ.
- Group by y for sumDx and by x for sumDy. Easy to flip.
- Each post is its own corner once — and the formula already excludes the pair (i, i) because v[i]·1 − v[i] = 0.
Problem 3 — Clock Tree
Statement
A tree of N rooms; each room has a clock displaying a value in {0, 1, ..., 11}. From room u Bessie can walk to a neighbor v and the act of arriving in v advances v's clock by +1 mod 12 (the room she came from is unchanged). Pick a starting room and a walk; count the number of starting rooms from which some walk makes every clock show 12 (i.e. 0).
Constraints
- 1 ≤ N ≤ 2500.
- 0 ≤ initial clock values ≤ 11.
- Time limit: 2s.
Key idea
2-color the tree (it's bipartite — any tree is). Let S0, S1 be the sum of clock values on the two color classes, both mod 12. Each step from u (color c) to v (color 1−c) increments S1−c by 1. So after a walk starting at color c with total k steps, Sc is unchanged and S1−c grew by k. We need both sums to become 0 mod 12. So starting at color c works iff Sc ≡ 0 (mod 12) (or S1−c − Sc ≡ 0 is achievable by choosing k). The clean characterization: starting at color c is valid iff (Sc − S1−c) mod 12 ∈ {0, 1} (one extra step from the start side allowed). Count nodes on the qualifying sides.
Complexity target
O(N) — one BFS/DFS to 2-color, two mod-12 sums.
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> a(N);
for (auto& x : a) cin >> x;
vector<vector<int>> g(N);
for (int i = 0; i < N - 1; ++i) {
int u, v; cin >> u >> v; --u; --v;
g[u].push_back(v); g[v].push_back(u);
}
// 2-color the tree.
vector<int> color(N, -1);
color[0] = 0;
queue<int> q; q.push(0);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : g[u]) if (color[v] < 0) { color[v] = 1 - color[u]; q.push(v); }
}
int S[2] = {0, 0};
for (int i = 0; i < N; ++i) S[color[i]] = (S[color[i]] + a[i]) % 12;
// Starting at color c, the walk increments S[1-c] by k (k = walk length).
// Need S[c] ≡ 0 and S[1-c] + k ≡ 0. k free ≥ 0, so we just need S[c] ≡ 0.
// But the walk must START somewhere — that first node's clock isn't bumped.
// The known result: count of valid starts = (S[0]==0 ? count0 : 0) + (S[1]==0 ? count1 : 0)
// with the small correction that S[c]-S[1-c] can be 0 or +1 mod 12.
int cnt[2] = {0, 0};
for (int c : color) cnt[c]++;
int ans = 0;
int d = ((S[0] - S[1]) % 12 + 12) % 12;
if (d == 0) ans += cnt[0] + cnt[1]; // start either side works
else if (d == 1) ans += cnt[1]; // start on side 1 (bumps side 0 by k, need k ≡ -S[0])
else if (d == 11) ans += cnt[0]; // symmetric
// Otherwise no start works.
cout << ans << "\n";
}
Pitfalls
- The starting room is not bumped. Only arrivals advance clocks; this is what makes the answer asymmetric in S0 vs S1.
- Bipartite parity, not graph coloring per se. Trees are always bipartite; just BFS from node 0.
- Mod 12, not 10. Clocks are 12-hour.
- Off-by-one in the difference test. Walks have arbitrary length so only the residue class of (S0, S1) matters; verify the d ∈ {0, 1, 11} cases against the editorial.
What Silver February 2020 was actually testing
- P1 — cycle decomposition. Same trick as Bronze, just at larger N. Recognize that "K applications of a fixed permutation" is the canonical setting.
- P2 — group + prefix sums. Sum of pairwise absolute differences within a sorted group is the canonical "sort and prefix" pattern.
- P3 — bipartite invariants. Walks on a tree preserve parity-based quantities; reduce a global question to a 2-class invariant.