Senの競技プログラミング備忘録

こけた問題を自分用の解説で載せる。けんちょんさんのブログを目指したい。質的にも量的にも。こけた問題だけに限定するけど

AGC039-A Connection and Disconnection

atcoder.jp

概要

100文字までの文字列SをK(1 <= K <= 10^9)回繰り返した文字列(アルファベット小文字のみ)をTとする。Tに隣接した文字が現れないようにするには、何文字置き換える必要がある?

ACするまで

1. つながってる文字は2, 4, 6文字目だけ消せばよさそう(大嘘)
2. 一種類しかないやつはめんどくさそうだなぁ(10分経過後に気づく)
3. 先頭と後尾が同じものは、Tの中でK-1回現れる。これのほかに先頭が1回分、後尾が1回分出てくる。あと、偶数文字目だけ消すじゃなくて、連続してる文字の数KでK/2を切り捨てした分だけ消すと考えたほうがよさそう。(ここまで80分経過して幾多のWAを重ねる)(は?)
4. 先頭と後尾が同じのもの、違うもの、そもそも1種類の文字しかないものの3通りで場合分けして通す。

考察

ACするまでの4番そのものである。
ちなみに、差分をうまく取ることで簡潔な別解がある。

本番通した解法

そもそも同じ

S.size() * T / 2を出力すればよい。

先頭と後尾は違う

Sの中身を見て、連続してる文字列の長さがtならば、t * K / 2を答えに足せばよい。先頭と後尾でつながることはないので、シンプル

先頭と後尾が同じ

Tの中では、次のように分けられる。

  • Sの先頭でつながってるもの
  • Sの後尾でつながってるもの
  • Sの先頭と後尾をつなげたもの * (K - 1)個

それぞれ丁寧に数え上げればよい。

ACコード
Submission #7874780 - AtCoder Grand Contest 039

別解

本番の解法と似てるように、つながってる文字列を

  • (1周目の文字列の頭にかかる回数) "aaabbca"なら、"aaa"に相当する1回 -> A
  • (K周目の文字列のしっぽにかかる回数) "aaabbca"なら、"a"に相当する0回 -> B
  • (2つの文字列の首尾がくっついてる部分にかかる回数) "aaabbca"なら、"aaabbcaaaabbca"の"aaaa"に相当する2回 -> C
  • (首尾ではない文字列の部分での1周期あたりの操作回数) "aaabbca"なら、"bb"に相当する1回 -> D

f(S)を文字列Sにおける操作回数と定めると、次の式が成り立つ
f(S) = A + B + D
f(S + S) = A + B + C + D * 2 (Sを二つ連結したものを渡してる)
よって、
f(S + S) - f(S) = C + D
となるのである。

ここで、
f(SをK回続けた文字列) = A + B + C * (K - 1) + D * K
= f(S) + (K - 1) * f(S + S)

との等式が成り立つ。(これ、コンテスト本番に思いつくのか?)
よって、この式に従って計算すればよい。

ここで、注意が必要なのは、同じ文字のみによって構成されてる場合である。その場合、AとBとCは厳密に区別できないので、この式から外れる。この場合だけ、事前に場合分けをすればよい。

ACソースコード
Submission #8760694 - AtCoder Grand Contest 039

一言

ICPCのやるだけをいっぱいやります。