Codeforces Round #666 (Div. 2) C. Multiples of Length
長さ()の配列()が与えられる。
操作を3回行って、全ての要素を0にせよ。
気づき
- 任意の長さの配列を3回の操作ですべてにできることが証明できると書いてあるので、選択肢は多くない。
- 選択した範囲の長さをとするとき、をゼロにできるというのは、がの倍数であること。
- 全ての要素をの倍数にできれば良さそう
2.については自明とする
1.について
まず、すべての要素について少なくとも2回は操作しないといけない。( 一回の操作で0になるのは、が運良くの倍数のときだけ)
よって考えられる範囲の決め方として、
- 任意の位置で区切って、「左、右、全体」
- 「全体、全体、全体」
しか考えられない(いまは順番のことは考えていない)
しかし、同じ範囲を連続で2回えらぶのは明らかに損なので、前者しか候補にならない。
( 全体を選ぶということはにの倍数を足すことなので、連続で選ぶなら、一回目での倍数を足せば同じ結果になる)
3.について
任意の位置で区切ったとき、左の範囲の長さを、右の範囲の長さをとする。
- に全体の長さの倍数を足して、左側をの倍数に、右側をの倍数にする。
- 左側の範囲にの倍数を足して、右側の範囲にの範囲をたして、全体をの倍数にする
のうち、どちらが実現できそうかを考える。
直感的に、一つの操作で2つの状態(の倍数、の倍数)へ遷移するより、1つの状態(の倍数)へ遷移したほうが素直な操作だと考える(微粒子レベルのエスパー)
よって、「左、右、全体」の順番で操作して、全体を0にすることを考える。
つまり、左側にの倍数を足して、右側にの倍数をたして、全体をの倍数にすることを考える。
以上より、全体をの倍数にすることを考える
単純にの倍数にするなら全てのをに変換できれば良い。(この問題のエスパー部分)
ひとまず、をの倍数として立式してみると、
は自然数かつは任意の自然数なので、(は素数かもしれない)
よって、のときとなり、のときは必ず0にすることができる( 任意のはの倍数)ので、、で全てのについて0にできることがわかる。
(のとき、のときとなり片方を考えれば良い)
したがって、
- ] を選び、を足す(を0にする)。
- ] を選び、を足す(をの倍数にする)。
- ] を選び、を足す(をにする)。
この順の操作で、任意のについて、すべての要素を0にできる。
(の倍数としてを使うことができることに留意)
ただし、(与えられる配列の長さが1)の場合の実装に注意が必要!