Problem A: Hardest Matrix Leap Year
Problem Summary & Key Intuition:
- Summary: Given an operational year Y and a calibration interval k, find the number of years until the next strict future multiple of k.
- Key Intuition: The next strict multiple is given by k — (Y % k).
Detailed Explanation & Complexity:
- Formula: Writing Y = q * k + r with 0 <= r < k, the distance is k — r.
- Time Complexity: O(1) per testcase.
- Space Complexity: O(1) extra space.
C++ Implementation:
#include <iostream>
using namespace std;
void solve() {
long long year, interval;
cin >> year >> interval;
long long remainder = year % interval;
long long answer = interval — remainder;
cout << answer << "\n";
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int test_cases;
cin >> test_cases;
while (test_cases--) {
solve();
}
return 0;
}
Problem B: Doctor Doom's Energy Stones
Problem Summary & Key Intuition:
- Summary: Given N stone weights and a container weight capacity K, maximize the total count of stones selected such that their total weight does not exceed K.
- Key Intuition: To maximize the item count under a fixed capacity limit, always pick the lightest available items first (classic greedy strategy).
Detailed Explanation & Complexity:
- Detailed Explanation: Sort the array of weights in ascending order w1 <= w2 <= ... <= wN. Greedily accumulate weights starting from the smallest until adding the next stone exceeds capacity K.
- Time Complexity: O(N log N) per testcase due to sorting.
- Space Complexity: O(N) space.
C++ Implementation:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void solve() {
int n;
long long capacity;
cin >> n >> capacity;
vector<long long> weights(n);
for (int i = 0; i < n; i++) {
cin >> weights[i];
}
sort(weights.begin(), weights.end());
long long current_sum = 0;
int count = 0;
for (int i = 0; i < n; i++) {
if (current_sum + weights[i] > capacity) {
break;
}
current_sum += weights[i];
count++;
}
cout << count << "\n";
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int test_cases;
cin >> test_cases;
while (test_cases--) {
solve();
}
return 0;
}
Problem C: The Doom Protocol
Problem Summary & Key Intuition:
- Summary: We may flip signs of a prefix of length p and a disjoint suffix of length q (p + q <= n) of an array. Flipping position i costs 2|a_i| if i is even and |a_i| if i is odd. Find the minimum total cost so that the array sums to exactly S, or output -1.
- Key Intuition: Flipping a_i changes the total sum by -2a_i. If total sum is T, the sum of flipped elements must equal K = (T — S) / 2. If T — S is odd, no solution exists.
Detailed Explanation & Complexity:
- Detailed Explanation: Precompute prefix sums/costs and suffix sums/costs. As prefix length p decreases from n down to 0, maintain a hash map storing the minimum cost to achieve each suffix sum seen so far. For each prefix p, query the map for the required remaining sum K — prefSum[p].
- Time Complexity: O(N) amortized per test case.
- Space Complexity: O(N) extra space.
C++ Implementation:
#include <iostream>
#include <vector>
#include <cmath>
#include <unordered_map>
using namespace std;
const long long INF = 1e18;
void solve() {
int n;
long long target_sum;
cin >> n >> target_sum;
vector<long long> a(n + 1);
vector<long long> flip_cost(n + 1);
long long total_sum = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
total_sum += a[i];
long long val = abs(a[i]);
flip_cost[i] = (i % 2 == 0) ? 2 * val : val;
}
long long diff = total_sum — target_sum;
if (diff % 2 != 0) {
cout << -1 << "\n";
return;
}
long long required_sum = diff / 2;
vector<long long> pref_sum(n + 1, 0), pref_cost(n + 1, 0);
for (int i = 1; i <= n; i++) {
pref_sum[i] = pref_sum[i — 1] + a[i];
pref_cost[i] = pref_cost[i — 1] + flip_cost[i];
}
vector<long long> suff_sum(n + 2, 0), suff_cost(n + 2, 0);
for (int i = n; i >= 1; i--) {
suff_sum[i] = suff_sum[i + 1] + a[i];
suff_cost[i] = suff_cost[i + 1] + flip_cost[i];
}
unordered_map<long long, long long> best_suffix_cost;
long long min_total_cost = INF;
for (int p = n; p >= 0; p--) {
int idx = p + 1;
if (idx <= n + 1) {
long long s_val = suff_sum[idx];
long long c_val = suff_cost[idx];
if (best_suffix_cost.find(s_val) == best_suffix_cost.end() || c_val < best_suffix_cost[s_val]) {
best_suffix_cost[s_val] = c_val;
}
}
long long need = required_sum — pref_sum[p];
if (best_suffix_cost.find(need) != best_suffix_cost.end()) {
long long candidate = pref_cost[p] + best_suffix_cost[need];
min_total_cost = min(min_total_cost, candidate);
}
}
if (min_total_cost >= INF) {
cout << -1 << "\n";
} else {
cout << min_total_cost << "\n";
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int test_cases;
cin >> test_cases;
while (test_cases--) {
solve();
}
return 0;
}
Problem D: Saturn's Moon Alignment
Problem Summary & Key Intuition:
- Summary: For each energy value A, find the smallest k >= 0 such that adding k^2 yields a perfect square. If impossible, set A = 1 with k = 0. Output max k used and modified array.
- Key Intuition: We need M^2 — k^2 = A <=> (M — k)(M + k) = A. Setting u = M — k and v = M + k, u * v = A with u and v having the same parity. Minimizing k = (v — u) / 2 requires choosing divisor u <= sqrt(A) as close to sqrt(A) as possible. If A % 4 == 2, no matching-parity factors exist, forcing A -> 1.
Detailed Explanation & Complexity:
- Detailed Explanation: Scan u downwards from floor(sqrt(A)) to 1. The first divisor found where A / u has matching parity gives the minimum gap v — u and thus minimal k.
- Time Complexity: O(N sqrt(max A)) time.
- Space Complexity: O(N) space.
C++ Implementation:
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
pair<long long, long long> solve_single(long long a) {
long long sq = round(sqrt((double)a));
if (sq * sq == a) {
return {0, a};
}
if (a % 2 != 0 && (a % 4 == 2)) {
return {0, 1};
}
if (a % 4 == 2) {
return {0, 1};
}
long long best_u = -1;
for (long long u = sq; u >= 1; u--) {
if (a % u == 0) {
long long v = a / u;
if ((v — u) % 2 == 0) {
best_u = u;
break;
}
}
}
if (best_u == -1) {
return {0, 1};
}
long long v = a / best_u;
long long k = (v — best_u) / 2;
long long m = (v + best_u) / 2;
return {k, m * m};
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<long long> final_energies(n);
long long max_k = 0;
for (int i = 0; i < n; i++) {
long long a;
cin >> a;
auto [k, energy] = solve_single(a);
max_k = max(max_k, k);
final_energies[i] = energy;
}
cout << max_k << "\n";
for (int i = 0; i < n; i++) {
cout << final_energies[i] << (i == n — 1 ? "" : " ");
}
cout << "\n";
return 0;
}
Problem E: Doctor Doom's Binary Dominion
Problem Summary & Key Intuition:
- Summary: Split binary string S of length N into K non-empty contiguous parts (each length <= 31) to maximize sum(i * value(P_i)).
- Key Intuition: Because each part length is bounded by 31 bits, we can use interval dynamic programming with memoization dp(pos, parts_left) where each state transitions into at most 31 choices.
Detailed Explanation & Complexity:
- Detailed Explanation: Base case dp(N, 0) = 0. For state (pos, parts_left), iterate ending index end from pos up to min(N-1, pos + 30). Compute binary integer value iteratively and transition to dp(end + 1, parts_left — 1).
- Time Complexity: O(31 * N * K) total operations.
- Space Complexity: O(N * K) space for memo table.
C++ Implementation:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <climits>
using namespace std;
const long long INF = 1e18;
long long solve_dp(const string &s, int pos, int parts_left, int total_k, vector<vector<long long>> &dp) {
int n = s.length();
if (pos == n) {
return parts_left == 0 ? 0 : -INF;
}
if (n — pos < parts_left || parts_left <= 0) {
return -INF;
}
if (dp[pos][parts_left] != -1) {
return dp[pos][parts_left];
}
int part_number = total_k — parts_left + 1;
long long best = -INF;
long long val = 0;
for (int end = pos; end < min(n, pos + 31); end++) {
val = (val << 1) + (s[end] — '0');
long long rest = solve_dp(s, end + 1, parts_left — 1, total_k, dp);
if (rest != -INF) {
best = max(best, part_number * val + rest);
}
}
return dp[pos][parts_left] = best;
}
void solve() {
int n, k;
cin >> n >> k;
string s;
cin >> s;
vector<vector<long long>> dp(n + 1, vector<long long>(k + 1, -1));
long long ans = solve_dp(s, 0, k, k, dp);
cout << ans << "\n";
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int test_cases;
cin >> test_cases;
while (test_cases--) {
solve();
}
return 0;
}
Problem F: Quantum Chain
Problem Summary & Key Intuition:
- Summary: Grid pathfinding from top-left to bottom-right. Pick one light frequency from sorted base array B shifted by cell energy G_{r,c}. Chosen frequencies must strictly increase along path. Minimize total cost sum(10^6 — F) or print DOOMED.
- Key Intuition: Maintain running prefix minimum arrays for candidate light frequencies. Monotonic two-pointer updates allow finding the best strictly smaller frequency predecessor from the top or left cell in amortized O(1) time.
Detailed Explanation & Complexity:
- Detailed Explanation: Sort base frequencies B ascending. Iterating through cells (r, c), maintain idx_top and idx_left pointers advancing monotonically to query pref[c][idx_top — 1] and pref[c — 1][idx_left — 1]. Use rolling 1D DP arrays over grid rows to optimize memory.
- Time Complexity: O(N * M * K) total time.
- Space Complexity: O(M * K) memory using rolling arrays.
C++ Implementation:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const long long INF = 1e18;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
if (!(cin >> n >> m >> k)) return 0;
vector<long long> b(k);
for (int i = 0; i < k; i++) {
cin >> b[i];
}
sort(b.begin(), b.end());
vector<long long> prev_grid(m), curr_grid(m);
vector<vector<long long>> prev_pref(m, vector<long long>(k, INF));
vector<vector<long long>> curr_pref(m, vector<long long>(k, INF));
for (int r = 0; r < n; r++) {
for (int c = 0; c < m; c++) {
cin >> curr_grid[c];
long long g_curr = curr_grid[c];
long long g_top = (r > 0) ? prev_grid[c] : 0;
long long g_left = (c > 0) ? curr_grid[c — 1] : 0;
long long running_min = INF;
int idx_top = 0, idx_left = 0;
for (int i = 0; i < k; i++) {
long long f = g_curr + b[i];
if (r > 0) {
while (idx_top < k && g_top + b[idx_top] < f) {
idx_top++;
}
}
if (c > 0) {
while (idx_left < k && g_left + b[idx_left] < f) {
idx_left++;
}
}
long long best_prev = INF;
if (r == 0 && c == 0) {
best_prev = 0;
} else {
if (r > 0 && idx_top > 0) {
best_prev = min(best_prev, prev_pref[c][idx_top — 1]);
}
if (c > 0 && idx_left > 0) {
best_prev = min(best_prev, curr_pref[c — 1][idx_left — 1]);
}
}
if (best_prev != INF) {
running_min = min(running_min, best_prev + (1000000LL — f));
}
curr_pref[c][i] = running_min;
}
}
for (int c = 0; c < m; c++) {
prev_grid[c] = curr_grid[c];
for (int i = 0; i < k; i++) {
prev_pref[c][i] = curr_pref[c][i];
}
}
}
long long ans = prev_pref[m — 1][k — 1];
if (ans >= INF) {
cout << "DOOMED\n";
} else {
cout << ans << "\n";
}
return 0;
}
Problem G: Doom's Palindromic Matrix
Problem Summary & Key Intuition:
- Summary: Calculate range sums of distinct palindromic subsequence counts f_x(i) over Pascal matrix rows i in [L, R] truncated at column x modulo 998244353.
- Key Intuition: The structural behavior of row i splits i into three main mathematical regions relative to limit x:
- Region 1 (i <= x): Full symmetric row, geometric sums of powers of 2 based on even/odd parity.
- Region 2 (x < i <= 2x+1): Partially folded row, capped symmetric pair counts.
- Region 3 (i >= 2x+2): Strictly increasing window, constant x + 1 distinct elements per row.
Detailed Explanation & Complexity:
- Detailed Explanation: Evaluate contributions from each region using logarithmic power function power_two and range exponent sum formula sum_pow.
- Time Complexity: O(Q log MOD) time.
- Space Complexity: O(1) space.
C++ Implementation:
#include <iostream>
#include <algorithm>
using namespace std;
const long long MOD = 998244353;
long long power_two(long long exp) {
long long res = 1;
long long base = 2;
while (exp > 0) {
if (exp % 2 == 1) res = (res * base) % MOD;
base = (base * base) % MOD;
exp /= 2;
}
return res;
}
long long sum_pow(long long low, long long high) {
if (low > high) return 0;
long long val_high = power_two(high + 1);
long long val_low = power_two(low);
return (val_high — val_low + MOD) % MOD;
}
void solve_query() {
long long l, r, x;
if (!(cin >> l >> r >> x)) return;
long long total = 0;
long long r1_start = l, r1_end = min(r, x);
if (r1_start <= r1_end) {
long long res = 0;
long long even_start = (r1_start % 2 == 0) ? r1_start : r1_start + 1;
long long even_end = (r1_end % 2 == 0) ? r1_end : r1_end — 1;
if (even_start <= even_end) {
long long count = (even_end — even_start) / 2 + 1;
long long sp = sum_pow(even_start / 2, even_end / 2);
res = (res + 3 * sp % MOD * (count % MOD)) % MOD;
}
long long odd_start = (r1_start % 2 != 0) ? r1_start : r1_start + 1;
long long odd_end = (r1_end % 2 != 0) ? r1_end : r1_end — 1;
if (odd_start <= odd_end) {
long long count = (odd_end — odd_start) / 2 + 1;
long long sp = sum_pow((odd_start + 1) / 2, (odd_end + 1) / 2);
res = (res + 2 * sp % MOD * (count % MOD)) % MOD;
}
total = (total + res) % MOD;
}
long long r2_start = max(l, x + 1), r2_end = min(r, 2 * x + 1);
if (r2_start <= r2_end) {
long long res = 0;
long long even_start = (r2_start % 2 == 0) ? r2_start : r2_start + 1;
long long even_end = (r2_end % 2 == 0) ? r2_end : r2_end — 1;
if (even_start <= even_end) {
long long sp = sum_pow(x — even_end / 2, x — even_start / 2);
res = (res + 3 * sp) % MOD;
}
long long odd_start = (r2_start % 2 != 0) ? r2_start : r2_start + 1;
long long odd_end = (r2_end % 2 != 0) ? r2_end : r2_end — 1;
if (odd_start <= odd_end) {
long long sp = sum_pow(x — (odd_end + 1) / 2, x — (odd_start + 1) / 2);
res = (res + 2 * sp) % MOD;
}
total = (total + res) % MOD;
}
long long r3_start = max(l, 2 * x + 2), r3_end = r;
if (r3_start <= r3_end) {
long long count = r3_end — r3_start + 1;
total = (total + (x + 1) % MOD * (count % MOD)) % MOD;
}
cout << total << "\n";
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int q;
if (cin >> q) {
while (q--) {
solve_query();
}
}
return 0;
}








