AtCoder Beginner Contest 124 C,D問題メモ
C - Coloring Colorfully
問題
- 左右一列に $N$ 枚のタイルが並ぶ
- 各タイルの色は長さ $N$ の文字列 $S$ によって与えられ、'0'なら黒、'1'なら白
- いくつかのタイルを塗り替えて、以下の条件を満たしたい
- どの隣り合う2枚のタイルも異なる色
- 塗り替える必要のある最小枚数を求めよ
- $1 \le N \le 10^5$
解法
最終的な状態は、'010101…
' か '101010…
' しかない。(長さは $N$ )
これが初期状態と違えば塗り替える必要がある。一つずつカウントする。
2種類の最終状態を両方試し、小さい方が答え。
'0', '1'を整数化した上で t
に前のタイルの色が入っているとして、t ^= 1
で0と1を反転できる。
def solve(init, s): t = init ret = 0 for c in s: if c != t: ret += 1 t ^= 1 return ret s = input() s = list(map(int, s)) print(min(solve(0, s), solve(1, s)))
D - Handstand
問題
- $N$ 個の'0'または'1'が並ぶ
- 最大 $K$ 回、以下の操作が行える
- 任意の区間 $[l, r]$ を選んで、その区間の'0'と'1'を反転させる
- 最終的に、'1' を最大何個並ばせることが出来るか求めよ
解法
こういう「'0'と'1'を区間で反転させることで要素を揃えていく」問題では、反転という操作は「異なる要素の境界を減らす」ことを意味すると考えると上手くいくことが多い。
111001011100010 ~~~~~ 111000100000010 ↑ ↑ こことここの境界が消えてる
今回、操作は $K$ 回まで行え、左右端とも自由に決められるので、最大 $2K$ 個の境界を無くせる。
無くす境界をどのように選ぶのがよいかというと、'1'を連続させるので、
11111 | 000 | 1 | 0000 | 111
こういう風な、'1'から始まる'1'と'0'交互の連続する境界について無くせばよい。
よって、'1'と'0'の連続する要素毎にカウントして、(連長圧縮)
1110001000000100 ↓ 111 000 1 000000 1 00 ↓ [ 3, 3,1, 6,1, 2, 0] ←便宜上、左右端が'0'だった場合は'1'が0個と考えて0を追加しておく
連続する $2K+1$ 要素の和の中で、最大のものを求めればよいことになる。 ただし両端はもともと '1' だった箇所でなければいけないので、0,2,4,…番目から始まる区間のみ調べる。
K = 2 S = 111 000 1 000000 1 00 11111 00 1 0 [ 3, 3, 1, 6, 1, 2, 5, 2, 1, 1, 0 ] ~~~~~~~~~~~~~~ = 14 ~~~~~~~~~~~~~~ = 15 ←答え ~~~~~~~~~~~~~~ = 11 ~~~~~~~~~~~~~~ = 9
区間の和は、累積和または尺取法を用いれば高速に行える。
from itertools import accumulate n, k = list(map(int, input().split())) s = input() def solve(): l = [] prev = '1' cnt = 0 for c in s: if prev == c: cnt += 1 else: l.append(cnt) cnt = 1 prev = c l.append(cnt) if prev == '0': l.append(0) w = 2 * k + 1 if len(l) <= w: return n acc = [0] + list(accumulate(l)) ans = 0 for i in range(0, len(l) - w + 1, 2): ans = max(ans, acc[i + w] - acc[i]) return ans print(solve())