Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Rewrite the scoring rule so every I is worth +1 by default, then subtract 2 exactly when it is immediately followed by X or V. This turns the weird Roman-ish rule into a plain adjacency bonus problem.
For minimizing, X and V are basically the same for adjacency purposes: both make a previous I become -1. The only difference is their own price, 10 vs 5, so after deciding how many non-I letters to place, use as many V as possible before spending on X.
Since I is cheap and also can create -2 discounts, the optimal solution always uses as many I letters as possible: i = min(c_I, number_of_question_marks). The remaining question marks must become X/V.
Now the only hard part is: if exactly k question marks become X/V, what is the maximum number of I -> X/V adjacencies you can create? Fixed characters also matter, so scan the string by blocks of consecutive ? and summarize each block.
For a block of len question marks, only the characters just before and just after the block matter. The maximum adjacency contribution as a function of k is almost linear, and across all blocks the only nontrivial choice is where to spend a few special X/V placements. Precompute a sorted list of marginal gains, then each query is just base value + letter costs - 2 * best_adjacencies.
Let's strip away the Roman cosplay first.
A letter I contributes:
+1 normally,-1 if the next character is X or V.So every I gives +1, and every adjacent pair
subtracts 2.
That means the whole value is:
where pairs is the number of positions j such that s[j] = I and s[j+1] is X or V.
Fixed letters are already fixed. For the question marks, we choose letters under per-query upper bounds.
First useful cleanup
For creating I -> big pairs, X and V are identical. Both are big letters. But their prices differ:
V costs 5,X costs 10.So once we know how many question marks become big letters, we should use as many V as possible, then use X only for the rest. No mystery there.
Also, I is always cheaper than V or X, and it can only help create discounts. Replacing an I by a big letter increases the raw letter cost by at least 4, while it can improve the pair count by at most 2 pairs, worth at most 4. So using more I is never worse.
Therefore each query has a forced count:
where m is the number of ? characters. The number of big letters among question marks is
Then the cheapest split among big letters is:
The raw added cost is:
Now all that remains is: with exactly k question marks turned into big letters, maximize the number of I -> big adjacent pairs.
That's the whole damn problem.
Block view
Question marks only interact locally. Break the string into maximal blocks of consecutive ? characters. For each block, only three things matter:
len,I,X or V).Inside a block, we place exactly k_block big letters and len - k_block I letters.
There are two sources of pairs:
For a block with len = L and b big letters:
b = 0, the whole block is I...I. It only gets a right-border pair if the character after the block is big.b = L, the whole block is big. It only gets a left-border pair if the character before the block is I.0 < b < L, we have both I and big letters inside. We can arrange them as III...IBBB...B, which creates exactly one internal I -> big pair. Also, if the previous fixed character is I, putting a big letter first gives a left-border pair. If the next fixed character is big, putting an I last gives a right-border pair.The annoying part is choosing which blocks get some big letters. But the function is simple enough to compress into marginal gains.
Start from k = 0
If all question marks are I, then every block followed by a fixed big letter contributes one pair at its right border.
Call that basePairs.
Now we add big letters one by one.
For each block:
So globally, for exactly k big letters, we need the best total marginal gain from spending k big placements.
For a block of length L, define:
left = 1 if the previous character is I, else 0,right = 1 if the next character is X or V, else 0.When L = 1, the block has only one position. Switching it from I to big:
right = 1,left = 1.So the single marginal gain is:
When L > 1, the first big letter can make the block mixed. Compared to all I, the gain is:
Why? One internal pair appears, and a left-border pair can appear. The right-border pair still exists because we can leave an I at the end.
The middle L-2 big letters have gain 0.
The final big letter makes the whole block big. Compared to the mixed state:
-1,right = 1: -right.So the last marginal gain is:
That looks like a knapsack, but it's barely wearing a fake mustache.
The first gain for every long block is either 1 or 2, always positive. The last gain is -1 or -2, always negative. Middle gains are zero.
Therefore:
For length-1 blocks, the only gain can be 1, 0, or -1; just throw it into the same marginal list idea.
A clean implementation is:
pos,neg,pos descending,neg descending too, because -1 is better than -2.Then build an array best[k]: maximum extra pairs after placing exactly k big letters into question marks.
The construction is:
Add each marginal to a running sum. Since all question marks get assigned, this gives best[k] for every 0 <= k <= m.
Finally:
Where fixedBase already includes:
X, V, I as if all I are +1,I -> big penalties are handled through basePairs too.The easiest way is to compute fixedBase = 10 * fixedX + 5 * fixedV + fixedI, and include all pairs involving fixed letters and question-mark blocks in the pair-count machinery.
Why this is correct
The key independence is that different ? blocks cannot affect each other except through the total number k of big letters. A block's contribution depends only on its own count of big letters and its two neighboring fixed characters.
For each block, the marginal gains have a rigid order:
1, creates useful structure,I in that block.So there is no hidden ordering trick. You cannot take the final negative step before filling the block, and you cannot use middle zeros before taking the first positive step in that same long block. But because every first step is positive and every final step is negative, the global greedy order is valid:
That gives the maximum possible number of discounted pairs for every exact k.
Then each query has a fixed number of I letters and big letters, and the cheapest X/V split is obvious. So the formula gives the minimum value.
Complexities:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
string s;
cin >> s;
int m = 0;
ll baseCost = 0;
ll basePairs = 0;
auto big = [&](char c) {
return c == 'X' || c == 'V';
};
for (int i = 0; i < n; i++) {
if (s[i] == '?') {
m++;
} else if (s[i] == 'X') {
baseCost += 10;
} else if (s[i] == 'V') {
baseCost += 5;
} else {
baseCost += 1;
}
}
for (int i = 0; i + 1 < n; i++) {
if (s[i] == 'I' && big(s[i + 1])) {
basePairs++;
}
}
vector<int> positive, negative;
int zeros = 0;
for (int l = 0; l < n; ) {
if (s[l] != '?') {
l++;
continue;
}
int r = l;
while (r < n && s[r] == '?') r++;
int len = r - l;
int leftI = (l > 0 && s[l - 1] == 'I');
int rightBig = (r < n && big(s[r]));
if (rightBig) basePairs++;
if (len == 1) {
int gain = leftI - rightBig;
if (gain > 0) positive.push_back(gain);
else if (gain == 0) zeros++;
else negative.push_back(gain);
} else {
positive.push_back(1 + leftI);
zeros += len - 2;
negative.push_back(-1 - rightBig);
}
l = r;
}
sort(positive.rbegin(), positive.rend());
sort(negative.rbegin(), negative.rend());
vector<ll> extra(m + 1, 0);
int used = 0;
ll cur = 0;
for (int gain : positive) {
cur += gain;
extra[++used] = cur;
}
for (int i = 0; i < zeros; i++) {
extra[++used] = cur;
}
for (int gain : negative) {
cur += gain;
extra[++used] = cur;
}
while (q--) {
int cX, cV, cI;
cin >> cX >> cV >> cI;
int useI = min(cI, m);
int k = m - useI;
int useV = min(cV, k);
int useX = k - useV;
ll letterCost = (ll)useI + 5LL * useV + 10LL * useX;
ll pairs = basePairs + extra[k];
cout << baseCost + letterCost - 2LL * pairs << '\n';
}
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
string s;
cin >> s;
int m = 0;
ll baseCost = 0;
ll basePairs = 0;
auto big = [&](char c) {
return c == 'X' || c == 'V';
};
for (int i = 0; i < n; i++) {
if (s[i] == '?') {
m++;
} else if (s[i] == 'X') {
baseCost += 10;
} else if (s[i] == 'V') {
baseCost += 5;
} else {
baseCost += 1;
}
}
for (int i = 0; i + 1 < n; i++) {
if (s[i] == 'I' && big(s[i + 1])) {
basePairs++;
}
}
vector<int> positive, negative;
int zeros = 0;
for (int l = 0; l < n; ) {
if (s[l] != '?') {
l++;
continue;
}
int r = l;
while (r < n && s[r] == '?') r++;
int len = r - l;
int leftI = (l > 0 && s[l - 1] == 'I');
int rightBig = (r < n && big(s[r]));
if (rightBig) basePairs++;
if (len == 1) {
int gain = leftI - rightBig;
if (gain > 0) positive.push_back(gain);
else if (gain == 0) zeros++;
else negative.push_back(gain);
} else {
positive.push_back(1 + leftI);
zeros += len - 2;
negative.push_back(-1 - rightBig);
}
l = r;
}
sort(positive.rbegin(), positive.rend());
sort(negative.rbegin(), negative.rend());
vector<ll> extra(m + 1, 0);
int used = 0;
ll cur = 0;
for (int gain : positive) {
cur += gain;
extra[++used] = cur;
}
for (int i = 0; i < zeros; i++) {
extra[++used] = cur;
}
for (int gain : negative) {
cur += gain;
extra[++used] = cur;
}
while (q--) {
int cX, cV, cI;
cin >> cX >> cV >> cI;
int useI = min(cI, m);
int k = m - useI;
int useV = min(cV, k);
int useX = k - useV;
ll letterCost = (ll)useI + 5LL * useV + 10LL * useX;
ll pairs = basePairs + extra[k];
cout << baseCost + letterCost - 2LL * pairs << '\n';
}
}
return 0;
}