人間だけど競プロやる

解けなかった問題を理解できたら記事を投稿します。日本語の解説が見つからなかったもの中心。

Codeforces Round #696 (Div. 2) D. Cleaning



左右から見て、スワップする場所をN通り試せるのだろうと思ったけど、それ以上詰めることが出来なかった。
気づき

  • スワップする場所を全通り試す
  • 石を減らす操作は貪欲に決まる
  • 左右から操作結果を記録する


数列に対して、なんらかの操作をした結果、変更した場所しか影響がないという問題はよくあって、これもその手の類問。
(よくあるのは、累積和をとっておいたり、左右から結果を持っておくなどがある。)

まず、
1. 操作をした結果、石は減るだけで、増えることはない。

1より、a_0(左端)の石を取り除くには、a_0 \le a_1である必要があり、石を取り除いた結果、a_0=0a_1^{\prime}=a_1-a_0となるし、a_0 > a_1のときは不適となる。
同様にa_1^{\prime}を取り除くとき、a_1^{\prime} \le a_2が必要であり……と、左端から順番に処理していくことになる。
さらに同じことが右端から操作するときにも言える。
以上より、
2. 左右から条件を満たす限り操作をおこない、途中で操作が出来ない場合、石をすべて取り除くことはできないと言って良い。また操作が可能であればその位置までは石を0に出来ている。


左からの操作結果を記録した配列をL、右からの操作を記録した配列をRとする。(任意のインデックス(の手前)まで操作したときに、そのインデックスに残っている石の数を記録しておく)

a_ka_{k+1}スワップしたときに何が起きるかを考える。
このとき、左からみていってL_{k-2}まではスワップ前と同じように操作できるので、影響はない。
同じように右から見ていってR_{k+3}までは影響がない。
よって、
3. L_{k-1}またはR_{k+2}までの時点で操作が出来ない場合は不適となる。
よって
4. a_ka_{k+1}スワップするときは、a_{k-1},a_k,a_{k+1},a_{k+2}の4つを実際にシミュレーションして判定すればよい。
a_{k-1},a_kは左からみてLを利用する。a_{k+1},a_{k+2}は右から見てRを利用する)



左右から操作をしたとき、全ての石を取り除くことができる条件は、ある位置a_la_{l+1}にたいして、
5. L_l = R_{l+1}かつL_{l-1} \ge 0R_{l+2} \ge 0となる。
\because 2.よりL_lR_{l+2}までに操作が不適になるのであれば当然不適。
そしてインデックスll+1まで操作ができるのであれば、最後は隣り合うふたつに同数の石があり、それらを取り除くことで全ての石を取り除くことができる。


実装上のコツ
添字の扱いで混乱するので、累積和をとるときのように、L、Rの最初は0を追加しておくとよい。
L、Rを計算するときに、L_k \ge a_{k+1}のときはそのままL_{k+1}=a_{k+1}-L_kとして、マイナスにしてしまってよい。その時点で操作は不適となるので、それ以降は「-1」としておく。
こうしておくとマイナスなら不適だったとして判定できる。

この方法でAC。