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.
Color segments are just transitions plus one. So stop staring at whole substrings and stare at the gaps between adjacent characters.
Flipping a length- substring changes only the two gaps at its ends. Every internal gap of that substring has both neighboring characters flipped, so it stays exactly the same.
Introduce dummy gaps and outside the rope. An operation starting at position toggles gaps and , which are exactly apart. Dummy gaps are allowed endpoints but contribute score.
Gaps with the same index modulo form independent chains: . Each operation toggles two adjacent vertices inside one such chain. The total answer is the sum of the best results over these chains.
For one chain, run DP left to right. If the current vertex is toggled oddly, you must decide whether to use the edge to the next vertex to fix/pass that oddness. Track operation count and current parity; add the vertex's final contribution when you leave it. This gives an solution overall.
The number of color segments is
So the real game is not about characters. It is about the gaps between characters.
Let
We want to maximize the number of s among these 's, then add at the end.
What One Operation Really Does
Suppose we flip the substring .
For a gap strictly inside the substring, both adjacent characters are flipped, so whether they are equal/different does not change. For a gap strictly outside, nothing changes.
Only the two boundary gaps can change:
To make the boundary cases clean, add two dummy gaps:
They contribute nothing to the score. Then every operation toggles exactly two gaps:
These two gap indices differ by exactly .
So the problem becomes:
We have vertices . Vertices have values and score if their final value is . Vertices have score . One operation toggles two vertices whose indices differ by . Use at most operations to maximize score.
That is the whole trick. The substring thing was wearing a fake mustache.
Splitting By Modulo
An operation connects gap with gap . Therefore indices never change modulo .
So each residue class forms an independent chain:
Inside one chain, an operation toggles two adjacent vertices of that chain.
Different chains do not interact except that they share the global operation budget .
Now we need, for each chain, compute:
Then we knapsack-combine all chains using at most operations.
DP For One Chain
Take one chain of vertices .
There is an edge between consecutive vertices. Choosing an edge means performing the corresponding length- flip, toggling both endpoints.
For a vertex , its final parity is affected by:
So while scanning left to right, we only need to know whether the current vertex has already been toggled by the previous edge.
Let mean: after processing up to the current position's incoming state, we have used used operations, and the current vertex has incoming toggle parity from the previous edge.
At vertex , choose whether to take the outgoing edge to :
Then the final value of this vertex is
If is a real gap, it contributes that final value. If it is dummy gap or , it contributes no matter what.
Then the next vertex has incoming parity .
For the last vertex, there is no outgoing edge, so only.
This computes the best score for this chain for every operation count.
Important detail: the global problem allows at most operations, not exactly . So after combining chains, take the maximum over all operation counts .
Why This Is Correct
Every original operation corresponds to toggling two gap vertices distance apart, and every such pair corresponds to exactly one valid length- substring flip. So the transformed graph problem is equivalent.
The graph splits into residue classes modulo , because every edge preserves the index modulo . Hence choices in different chains are independent, except for the total operation count.
For one chain, each operation is exactly choosing one edge. The final value of a vertex depends only on the chosen edge to its left and the chosen edge to its right. When scanning left to right, the left edge is already known as parity , and the right edge is the current binary decision . Therefore the chain DP considers every possible subset of edges exactly once and scores it correctly.
Finally, knapsack over chains tries every distribution of the operation budget among independent chains. That gives the global optimum.
No magic, just gap algebra. Annoyingly elegant, as usual.
Complexity
Each chain DP costs proportional to
The total length of all chains is , so all chain DPs together cost .
Combining chains also costs total if we only keep reachable operation counts up to .
Memory usage is per DP array, plus small chain storage.
#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 n, m, k;
cin >> n >> m >> k;
string s;
cin >> s;
vector<int> val(n + 1, 0), weight(n + 1, 0);
for (int i = 1; i < n; i++) {
val[i] = (s[i - 1] != s[i]);
weight[i] = 1;
}
const int NEG = -1000000000;
vector<int> global(m + 1, NEG);
global[0] = 0;
int globalLimit = 0;
for (int r = 0; r < k; r++) {
vector<int> chain;
for (int x = r; x <= n; x += k) chain.push_back(x);
vector<array<int, 2>> dp(m + 1), ndp(m + 1);
for (int j = 0; j <= m; j++) dp[j] = {NEG, NEG};
dp[0][0] = 0;
int localLimit = 0;
for (int i = 0; i < (int)chain.size(); i++) {
for (int j = 0; j <= m; j++) ndp[j] = {NEG, NEG};
for (int used = 0; used <= localLimit; used++) {
for (int in = 0; in < 2; in++) {
if (dp[used][in] <= NEG / 2) continue;
int maxOut = (i + 1 < (int)chain.size()) ? 1 : 0;
for (int out = 0; out <= maxOut; out++) {
if (used + out > m) continue;
int id = chain[i];
int add = weight[id] ? (val[id] ^ in ^ out) : 0;
ndp[used + out][out] = max(ndp[used + out][out], dp[used][in] + add);
}
}
}
localLimit = min(m, localLimit + (i + 1 < (int)chain.size()));
dp.swap(ndp);
}
vector<int> best(localLimit + 1, NEG);
for (int used = 0; used <= localLimit; used++) best[used] = dp[used][0];
vector<int> nextGlobal(m + 1, NEG);
for (int a = 0; a <= globalLimit; a++) {
if (global[a] <= NEG / 2) continue;
for (int b = 0; b <= localLimit && a + b <= m; b++) {
if (best[b] <= NEG / 2) continue;
nextGlobal[a + b] = max(nextGlobal[a + b], global[a] + best[b]);
}
}
globalLimit = min(m, globalLimit + localLimit);
global.swap(nextGlobal);
}
int transitions = 0;
for (int used = 0; used <= m; used++) transitions = max(transitions, global[used]);
cout << transitions + 1 << '\n';
}#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 n, m, k;
cin >> n >> m >> k;
string s;
cin >> s;
vector<int> val(n + 1, 0), weight(n + 1, 0);
for (int i = 1; i < n; i++) {
val[i] = (s[i - 1] != s[i]);
weight[i] = 1;
}
const int NEG = -1000000000;
vector<int> global(m + 1, NEG);
global[0] = 0;
int globalLimit = 0;
for (int r = 0; r < k; r++) {
vector<int> chain;
for (int x = r; x <= n; x += k) chain.push_back(x);
vector<array<int, 2>> dp(m + 1), ndp(m + 1);
for (int j = 0; j <= m; j++) dp[j] = {NEG, NEG};
dp[0][0] = 0;
int localLimit = 0;
for (int i = 0; i < (int)chain.size(); i++) {
for (int j = 0; j <= m; j++) ndp[j] = {NEG, NEG};
for (int used = 0; used <= localLimit; used++) {
for (int in = 0; in < 2; in++) {
if (dp[used][in] <= NEG / 2) continue;
int maxOut = (i + 1 < (int)chain.size()) ? 1 : 0;
for (int out = 0; out <= maxOut; out++) {
if (used + out > m) continue;
int id = chain[i];
int add = weight[id] ? (val[id] ^ in ^ out) : 0;
ndp[used + out][out] = max(ndp[used + out][out], dp[used][in] + add);
}
}
}
localLimit = min(m, localLimit + (i + 1 < (int)chain.size()));
dp.swap(ndp);
}
vector<int> best(localLimit + 1, NEG);
for (int used = 0; used <= localLimit; used++) best[used] = dp[used][0];
vector<int> nextGlobal(m + 1, NEG);
for (int a = 0; a <= globalLimit; a++) {
if (global[a] <= NEG / 2) continue;
for (int b = 0; b <= localLimit && a + b <= m; b++) {
if (best[b] <= NEG / 2) continue;
nextGlobal[a + b] = max(nextGlobal[a + b], global[a] + best[b]);
}
}
globalLimit = min(m, globalLimit + localLimit);
global.swap(nextGlobal);
}
int transitions = 0;
for (int used = 0; used <= m; used++) transitions = max(transitions, global[used]);
cout << transitions + 1 << '\n';
}