AT274の技術だったりなかったり

あっとのTECH LOG

競プロのこととか技術のこととか。たまに日常を書きます。

ABC161 E - Yutori

問題原文

atcoder.jp

問題要旨

明日から  N 日間のうち、  K 日間を選んで働く。
ただし、 x がつけられた日と、最後に働いてから  C 日間は働けない。
 K 日働くために必ず働かなければいけない日を全て求めよ。

  •   1 \leq N \leq 2 × 10^{5}

解法

まず問題を簡単にして、「できるだけ多く働くには?」を考える。これは貪欲的に働ける日に働くのが最適。 次に、 「 i 日目に働かなくても  K 日間働くことができるか?」を各   i について考えたくなるが、これはまともにやると [tex: O(N2)] になってしまう。

よく考えると、 「 i 日目に働かなくても  K 日間働くことができるか?」は、  i - 1 日目まで出来るだけ多く働く」+「 i + 1 日目以降できるだけ多く働く」をして、  K 日以上働いているか? と言い換えることができる。

ここまで見えると、ある点で2分される場合のdpが思い浮かぶので、 dpテーブル を二つ計算して各  i について高速に答えを求めれば良い。

↓は僕が解説を書いた類題 at274.hatenablog.com

実装

N, K, C = map(int, input().split())
S = list(input())


dp1 = [0] * (N + 1)  # iまでで何回働けるか
dp1[0] = 0
for i, s in enumerate(S, start=1):
    if s == 'x':
        dp1[i] = dp1[i - 1]
        continue
    b = max(0, i - C - 1)
    dp1[i] = dp1[b] + 1


dp2 = [0] * (N + 1)  # i以降で何回働けるか
for i in reversed(range(N)):
    s = S[i]
    if s == 'x':
        dp2[i] = dp2[i + 1]
        continue
    b = min(N, i + C + 1)
    dp2[i] = dp2[b] + 1


ans = []
# i番目のやつを使わなくてもK個取れるか?
for i in range(1, N + 1):
    if dp1[i - 1] + dp2[i] < K:
        ans.append(i)

print(*ans, sep="\n")

感想

ACまで23分だけど、発想が出るのが少し遅かったので一瞬で出したいなぁと。
想定は前と後ろから貪欲をして結果をマージするらしいです(やったこと似てるけど届かない壁を感じる)