CF 1550B - Maximum Cost Deletion

We are given a binary string and repeatedly remove substrings that consist of only one character type. Every deletion of a substring of length l gives a l + b points. The string shrinks after each operation because the remaining parts are concatenated together.

CF 1550B - Maximum Cost Deletion

Rating: 1000
Tags: greedy, math
Solve time: 3m 39s
Verified: yes

Solution

Problem Understanding

We are given a binary string and repeatedly remove substrings that consist of only one character type. Every deletion of a substring of length l gives a * l + b points. The string shrinks after each operation because the remaining parts are concatenated together.

The goal is not to find a sequence of deletions, but to compute the maximum possible total score after the entire string has been erased.

The first thing to notice is that every character is deleted exactly once. No matter how we choose the operations, the sum of all deleted lengths is always n. Because of that, the contribution from a is fixed:

a * n

The only part affected by our choices is how many deletion operations we perform. If we perform k deletions, the total score becomes:

a * n + b * k

This observation completely changes the problem. We no longer care about the exact lengths of deleted substrings. We only care about maximizing or minimizing the number of operations, depending on the sign of b.

The constraints are very small. The string length is at most 100, and there are at most 2000 test cases. Even fairly inefficient solutions would fit comfortably. Still, the intended solution is a simple greedy observation running in linear time per test case.

A few edge cases are easy to mishandle if we focus only on deletions instead of the score formula.

Consider:

n = 5
a = 3
b = 4
s = 11111

A careless solution might delete the whole string in one operation and score 15 + 4 = 19. The correct answer is 5 * (3 + 4) = 35, because when b > 0, every extra operation gives additional points, so deleting one character at a time is optimal.

Consider:

n = 6
a = 1
b = -5
s = 000000

Deleting one character at a time gives six operations and incurs the negative bonus six times. The optimal strategy is deleting the whole string at once, giving:

1 * 6 + (-5) = 1

Another subtle case is an alternating string:

n = 5
a = 0
b = -1
s = 01010

At first glance it seems we are forced to perform five deletions because there are five runs. In reality, after deleting all occurrences of one character type, the remaining characters merge into one block. The optimal answer comes from the run-count formula discussed later and equals -3, not -5.

Approaches

A brute-force approach would try all possible valid deletions and recursively explore the resulting strings. This is correct because every legal sequence of operations is examined. Unfortunately, the number of possible deletion sequences grows exponentially. Even for strings of length 100, the search space is astronomically large and completely infeasible.

The key observation is that the total contribution from lengths is fixed. Every character contributes exactly a once, regardless of the deletion order. The entire optimization problem reduces to choosing the number of operations.

The score can be rewritten as:

a * n + b * k

where k is the number of deletions.

If b >= 0, every additional deletion increases the score. The best strategy is to maximize k. Since every single character is itself a valid monochromatic substring, we can delete characters one by one and achieve:

k = n

The interesting case is b < 0. Now every operation costs points, so we want as few deletions as possible.

Let the string consist of runs maximal blocks of equal characters.

For a binary string, we can always delete all runs of one character type first. After that, all remaining runs merge together into a single run and can be removed in one final operation.

If there are runs blocks, then one character appears in at most runs / 2 blocks. We choose the character with fewer blocks.

The minimum possible number of operations becomes:

floor(runs / 2) + 1

A well-known equivalent formula is:

max(count_0_runs, count_1_runs)

Since:

max(count_0_runs, count_1_runs)
= floor(runs / 2) + 1

for any binary string.

Once we know the optimal number of operations, computing the answer is immediate.

Approach Time Complexity Space Complexity Verdict
Brute Force Exponential Exponential Too slow
Optimal O(n) O(1) Accepted

Algorithm Walkthrough

  1. Read n, a, b, and the binary string.
  2. If b >= 0, return:
n * (a + b)

This corresponds to deleting each character separately, giving exactly n operations. 3. If b < 0, count the number of runs in the string.

A run starts at position 0 and whenever s[i] != s[i-1]. 4. Compute:

operations = runs // 2 + 1

This is the minimum achievable number of deletions for a binary string. 5. Return:

a * n + b * operations

After counting runs, the answer follows directly from the score formula.

Why it works

The total deleted length over the entire process is always n, so every strategy earns exactly a * n points from the length term. The only variable part is b multiplied by the number of operations.

When b >= 0, increasing the number of operations always improves the score. Deleting one character at a time achieves the maximum possible value k = n.

When b < 0, fewer operations are better. In a binary string, deleting all runs of the less frequent character first causes the remaining runs to merge into one block. If there are runs total blocks, the less frequent character occupies floor(runs / 2) runs. Those runs are removed individually, and the merged remainder is removed once more, giving floor(runs / 2) + 1 operations. No strategy can do better, so this is the minimum possible number of deletions.

Since the algorithm always uses the optimal value of k, it always produces the maximum score.

Python Solution

import sys
input = sys.stdin.readline

t = int(input())

for _ in range(t):
    n, a, b = map(int, input().split())
    s = input().strip()

    if b >= 0:
        print(n * (a + b))
    else:
        runs = 1
        for i in range(1, n):
            if s[i] != s[i - 1]:
                runs += 1

        operations = runs // 2 + 1
        print(a * n + b * operations)

The first branch handles the easy case where every extra deletion is beneficial. Since deleting individual characters is always allowed, the optimal number of operations is exactly n.

The second branch counts runs. A run boundary occurs whenever adjacent characters differ. Starting from one run and counting transitions avoids off-by-one mistakes.

After obtaining the number of runs, we apply the formula runs // 2 + 1 for the minimum possible deletions. The final score is then computed directly from a * n + b * operations.

No special handling is needed for strings of length one. The run counter correctly remains equal to one, producing one operation.

Worked Examples

Example 1

Input:

n = 5
a = -2
b = 5
s = 11001

Since b > 0, we immediately use the first case.

Variable Value
n 5
a -2
b 5
Strategy maximize operations
Operations 5
Answer 5 × (-2 + 5) = 15

This example demonstrates that when b is positive, the string structure does not matter at all. Only the length matters.

Example 2

Input:

n = 6
a = 1
b = -4
s = 100111

Count runs.

Position Character Run Count
0 1 1
1 0 2
2 0 2
3 1 3
4 1 3
5 1 3

Now compute the answer.

Variable Value
runs 3
operations 3 // 2 + 1 = 2
a × n 6
b × operations -8
Answer -2

This trace shows the key greedy idea. Even though the string contains three runs, only two deletions are necessary.

Complexity Analysis

Measure Complexity Explanation
Time O(n) One scan of the string to count runs
Space O(1) Only a few variables are stored

With n ≤ 100 and at most 2000 test cases, the solution performs only a few hundred thousand character comparisons in total. It is comfortably within both the time and memory limits.

Test Cases

# helper: run solution on input string, return output string
import sys, io

def solve():
    input = sys.stdin.readline

    t = int(input())
    ans = []

    for _ in range(t):
        n, a, b = map(int, input().split())
        s = input().strip()

        if b >= 0:
            ans.append(str(n * (a + b)))
        else:
            runs = 1
            for i in range(1, n):
                if s[i] != s[i - 1]:
                    runs += 1
            ans.append(str(a * n + b * (runs // 2 + 1)))

    sys.stdout.write("\n".join(ans))

def run(inp: str) -> str:
    sys.stdin = io.StringIO(inp)
    sys.stdout = io.StringIO()
    solve()
    return sys.stdout.getvalue()

# provided samples
assert run(
"""3
3 2 0
000
5 -2 5
11001
6 1 -4
100111
"""
) == "6\n15\n-2"

# minimum size
assert run(
"""1
1 3 -2
0
"""
) == "1"

# all equal, negative b
assert run(
"""1
6 1 -5
000000
"""
) == "1"

# alternating pattern
assert run(
"""1
5 0 -1
01010
"""
) == "-3"

# maximum operations beneficial
assert run(
"""1
4 -1 10
1010
"""
) == "36"

# length 100, all same
assert run(
"""1
100 1 -100
""" + "0" * 100 + "\n"
) == "0"
Test input Expected output What it validates
n=1, s=0, a=3, b=-2 1 Minimum-size string
000000, b<0 1 Single run, one deletion
01010, b=-1 -3 Alternating runs and run formula
1010, b>0 36 Positive bonus, maximize operations
Length 100 all zeros 0 Largest length with one run

Edge Cases

Single-character string

Input:

1
1 3 -2
0

The string has one run.

runs = 1
operations = 1 // 2 + 1 = 1
answer = 3 * 1 + (-2) * 1 = 1

The algorithm correctly handles the smallest possible input without requiring any special case.

All characters equal

Input:

1
6 1 -5
000000

There is exactly one run.

runs = 1
operations = 1
answer = 6 - 5 = 1

A naive strategy that deletes characters individually would perform six operations and lose extra points. The formula correctly identifies that one deletion is optimal.

Alternating string

Input:

1
5 0 -1
01010

Run counting gives:

runs = 5
operations = 5 // 2 + 1 = 3
answer = -3

Although there are five runs initially, deleting all runs of one character type first allows the remaining runs to merge. This is exactly the scenario where many incorrect solutions overestimate the minimum number of operations.

Positive bonus with one run

Input:

1
5 2 3
11111

Since b > 0, the algorithm ignores run structure and computes:

answer = 5 * (2 + 3) = 25

Deleting one character at a time yields five operations, which is better than deleting the whole string at once. The positive-bonus branch captures this behavior automatically.