2024 January Bronze — three problems
← 2024 January contest index · Official results page
Bronze is ad-hoc and simulation: the algorithmic content lives in one observation each, and the rest is clean code.
Problem 1 · Majority Opinion
Official statement (cpid=1371)
Statement (paraphrased)
There are N cows in a row, each preferring one of K hay types. Farmer John can call a contiguous focus group [l, r]; if some hay type is preferred by strictly more than half the cows in that group, every cow in [l, r] switches to that hay type. Output every hay type that can end up as the unanimous preference of all N cows after some sequence of focus groups, in sorted order, or "-1" if none exists.
Constraints
- 2 ≤ N ≤ 105, sum of N over test cases ≤ 2·105
- Multiple test cases per input
- Time/memory: USACO Bronze default (2 s / 256 MB)
Key idea
A hay type h is achievable iff some contiguous run contains at least two adjacent positions of h inside a window where h is already a strict majority. The cleanest characterisation: h can win iff there exist three positions i < j < k of hay h with j ≤ i + 2, i.e. two of the three closest occurrences of h are within distance 2. Then that triple seeds a small majority window, which you can grow outward repeatedly.
Implementation: for each hay type, scan its occurrence list and check whether any three consecutive occurrences fit in a window of length ≤ 5. Equivalently, two consecutive occurrences within distance 2 are enough to seed it.
Complexity
O(N) per test case after bucketing by hay type.
C++ reference
#include <bits/stdc++.h>
using namespace std;
int main() {
int T; scanf("%d", &T);
while (T--) {
int N; scanf("%d", &N);
vector<int> a(N);
for (auto& x : a) scanf("%d", &x);
// group indices by hay type
unordered_map<int, vector<int>> pos;
for (int i = 0; i < N; i++) pos[a[i]].push_back(i);
vector<int> good;
for (auto& [h, v] : pos) {
bool ok = false;
for (int i = 0; i + 2 < (int)v.size(); i++)
if (v[i + 2] - v[i] <= 4) { ok = true; break; } // 3 occurrences in 5 cells
for (int i = 0; i + 1 < (int)v.size() && !ok; i++)
if (v[i + 1] - v[i] <= 2) ok = true; // 2 adjacent within 2
if (ok) good.push_back(h);
}
if (good.empty()) { puts("-1"); continue; }
sort(good.begin(), good.end());
for (int x : good) printf("%d\n", x);
}
}
Pitfalls
- "Strictly more than half" — a tie does not convert the window.
- Forgetting to sort the output. The judge wants ascending hay-type IDs.
- Reading multiple test cases in one input file. Bronze sometimes hides the T loop in the format.
Problem 2 · Cannonball
Official statement (cpid=1372)
Statement (paraphrased)
Bessie starts at position S on a number line with power P = 1, moving right. Some cells are jump pads with a value v: when Bessie lands on one, her power is set to max(P, v) and her direction reverses. Other cells are targets with a strength s: if Bessie lands on one with P ≥ s, the target breaks (becomes empty) and she keeps moving in her current direction; otherwise the target stops her. Each step she moves P cells in her current direction. Count the total number of targets broken before she exits the number line or stops on an unbreakable target.
Constraints
- 1 ≤ N ≤ 105 cells
- Time/memory: USACO Bronze default (2 s / 256 MB)
Key idea
Pure simulation. The only subtlety: Bessie's power is monotonically non-decreasing (jump pads only raise it), and each landing either ends the process or strictly progresses simulation, so total steps are bounded by O(N) hop-events when implemented as "jump in direction d until you hit a non-empty cell." A naive cell-by-cell loop is O(N · P) and TLEs at the upper bound.
Maintain two ordered sets (e.g. std::set<int>) of "occupied" positions: one indexed left-to-right,
one right-to-left. At each step, find the next occupied cell in the current direction; if its position has the
same parity-step alignment Bessie would land on (i.e. distance divisible by P), interact with it, else she
flies past and exits.
Complexity
O(N log N) using std::set lookups; each occupied cell is visited O(1) times.
C++ reference
#include <bits/stdc++.h>
using namespace std;
int main() {
int N, S; scanf("%d %d", &N, &S);
// cell[i] = 0 empty, >0 jump pad value, <0 -strength target
vector<long long> cell(N + 2, 0);
for (int i = 1; i <= N; i++) scanf("%lld", &cell[i]); // [verify] input format cpid=1372
set<int> occ;
for (int i = 1; i <= N; i++) if (cell[i] != 0) occ.insert(i);
long long P = 1;
int dir = +1, pos = S, broken = 0;
while (true) {
auto it = (dir > 0) ? occ.lower_bound(pos + 1) : occ.upper_bound(pos - 1);
if (dir > 0 ? it == occ.end() : it == occ.begin()) break;
if (dir < 0) --it;
int np = *it;
if (abs(np - pos) % P != 0) break; // she steps over it, off the line
pos = np;
long long v = cell[np];
if (v > 0) { P = max(P, v); dir = -dir; } // jump pad
else if (-v <= P) { broken++; occ.erase(it); cell[np] = 0; } // target breaks
else break; // unbreakable
}
printf("%d\n", broken);
}
Pitfalls
- Power can grow large; use
long longfor P and target strengths. - Bessie does not interact with cells she skips over (her hop length is P, not 1).
- After a target breaks, that cell is empty for future hops — remove it from the set.
Problem 3 · Balancing Bacteria
Official statement (cpid=1373)
Statement (paraphrased)
Patches 1..N have bacteria levels a[1..N]. A single spray applied at the rightmost patch with power L changes a[N] by ±L, a[N−1] by ±(L−1), ..., a[N−L+1] by ±1. (Sign chosen per spray.) Find the minimum total number of sprays so every a[i] becomes 0. The answer is guaranteed ≤ 109.
Constraints
- 1 ≤ N ≤ 2·105; |ai| ≤ 1015
- Answer fits in 109
- Time/memory: USACO Bronze default (2 s / 256 MB)
Key idea
Take two differences. The first difference di = ai − ai−1 is incremented by ±1 per spray for every position covered. The second difference ei = di − di−1 changes by ±1 only at the left boundary of the spray's range. So sweeping left-to-right while tracking the cumulative effect of past sprays, the required net number of sprays at each boundary is forced. Sum the absolute values of those forced counts.
Complexity
O(N), one linear pass over second differences.
C++ reference
#include <bits/stdc++.h>
using namespace std;
int main() {
int N; scanf("%d", &N);
vector<long long> a(N + 1, 0);
for (int i = 1; i <= N; i++) scanf("%lld", &a[i]);
// We want every a[i] = 0. A spray with power L at the right end adds an
// arithmetic progression ending at +/-L on position N, slope 1 going left.
// Equivalently, after two prefix-difference transforms each spray becomes a
// single point change at position N-L+1. Process from right to left.
long long ans = 0, slope = 0, val = 0;
for (int i = N; i >= 1; i--) {
val += a[i] + slope; // current bacteria after past sprays' tails
// [verify] sign/index convention against editorial cpid=1373
long long k = -val; // sprays starting at this left boundary
ans += llabs(k);
slope += k; // each such spray adds slope 1 to cells further left
val = 0;
}
printf("%lld\n", ans);
}
Pitfalls
long longeverywhere — intermediate values blow past 32 bits even though the final answer fits in 109.- The "sprays" are unsigned in count but signed in effect; track the signed net per boundary, then sum |net|.
- Indexing direction matters: the spray template grows leftward from position N, so iterate from right to left.