2022 February Bronze · All three problems
Problem 1 · Sleeping in Class
Statement (paraphrased)
Elsie keeps a log a1,…,aN of how many times Bessie nods off in each class period. The only allowed modification is to merge two adjacent entries into their sum. Across at most T = 10 independent test cases, find the minimum number of merges needed so every remaining entry is the same value.
Constraints
- 1 ≤ T ≤ 10 test cases.
- 1 ≤ N ≤ 105; 0 ≤ ai ≤ 106; total Σai ≤ 106 per case.
- Time / memory: 2 s, 256 MB (default).
Key idea
The final array consists of k equal values summing to S = Σa, so the only candidate target values are divisors of S. For each divisor d of S, scan left to right accumulating a prefix sum; whenever the prefix hits a multiple of d that block is closeable. If the prefix ever exceeds the next multiple, d fails. The minimum merge count for a working d is N − (S/d). Maximise S/d (i.e. minimise d) over working divisors. Edge: if every ai=0 the answer is 0.
Complexity target
O(N · σ(S)) per case. σ(106) ≤ 240, comfortable.
C++ reference solution
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
int T; cin >> T;
while (T--) {
int N; cin >> N;
vector<ll> a(N);
ll S = 0;
for (auto& x : a) { cin >> x; S += x; }
if (S == 0) { cout << 0 << '\n'; continue; }
auto works = [&](ll d) -> bool {
ll run = 0;
for (ll x : a) {
run += x;
if (run == d) run = 0;
else if (run > d) return false;
}
return true;
};
ll best = 0; // best S/d (== number of blocks)
for (ll d = 1; d * d <= S; ++d) if (S % d == 0) {
if (works(d)) best = max(best, S / d);
if (works(S / d)) best = max(best, d);
}
cout << (N - best) << '\n';
}
return 0;
}
Pitfalls
- Zero-sum case (all ai=0) — any merge count works; output 0.
- Iterate divisors via d·d ≤ S, but test both d and S/d as candidate targets.
- The number of merges is N minus the number of blocks; don't off-by-one this.
Problem 2 · Photoshoot 2
Statement (paraphrased)
Two permutations a, b of {1..N}. In one operation you may pick any cow and shift it to the left by any positive number of positions. Find the minimum number of operations to transform a into b.
Constraints
- 1 ≤ N ≤ 105.
- Subtasks: cases 3–6: N ≤ 100; 7–10: N ≤ 5000; 11–14: full.
- Time / memory: 2 s, 256 MB (default).
Key idea
Cows that don't move form a contiguous subsequence of b appearing as a subsequence of a in the same order — every other cow has to be slid left into place. So the answer is N − L, where L is the longest prefix of b matchable as a subsequence of a. Walk a left-to-right with a pointer j into b; advance j whenever a[i] == b[j]. Then L = j.
Complexity target
O(N). Single pass with two pointers.
C++ reference solution
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N; cin >> N;
vector<int> a(N), b(N);
for (auto& x : a) cin >> x;
for (auto& x : b) cin >> x;
int j = 0;
for (int i = 0; i < N; ++i)
if (a[i] == b[j]) ++j;
// j cows already aligned; the remaining N - j must each be moved left once.
cout << (N - j) << '\n';
return 0;
}
Pitfalls
- It is not LCS in the general sense — because each operation moves a cow strictly left, the "fixed" cows must appear in order in both a and b as a prefix of b. The greedy two-pointer is exact.
- Don't confuse "minimum left-shifts" with "minimum swaps"; this is not an inversion count.
- Read input fast; N up to 105.
Problem 3 · Blocks
Statement (paraphrased)
Bessie has 4 cubes, each labelled with 6 letters. For each of N query words (length 1–4) decide whether the word can be spelled by assigning each letter to a distinct cube that contains that letter.
Constraints
- 1 ≤ N ≤ 10. Words have length 1..4 and use uppercase letters.
- Time / memory: 2 s, 256 MB (default).
Key idea
Length ≤ 4, so try all 4! = 24 cube permutations and check each. Alternatively a 4-cube bitmask: for each letter precompute the set of cubes containing it, then DFS/permute through the word picking an unused cube per letter. Either way it's effectively O(24·|word|) per query — instant.
Complexity target
O(N · 24 · 4) per test. Brute force is the intended solution.
C++ reference solution
#include <bits/stdc++.h>
using namespace std;
int main(){
int N; cin >> N;
vector<string> block(4);
for (auto& s : block) cin >> s;
while (N--) {
string w; cin >> w;
int perm[4] = {0,1,2,3};
bool ok = false;
do {
bool good = true;
for (int i = 0; i < (int)w.size(); ++i) {
if (block[perm[i]].find(w[i]) == string::npos) { good = false; break; }
}
if (good) { ok = true; break; }
} while (next_permutation(perm, perm + 4));
cout << (ok ? "YES" : "NO") << '\n';
}
return 0;
}
Pitfalls
- A cube can be used at most once per word — that's the whole point of MOO returning NO when only one block has an M.
- Words shorter than 4 still need 4! permutations of which cube goes to which position;
next_permutationover [0,1,2,3] handles this since unused positions are ignored. - Letters are uppercase only; no need to handle case.