競プロ備忘録

競プロerの備忘録

ABC270E - Apple Baskets on Circle

公式解説では二分探索をうまく適用する賢い方法がとられていますが、私はそんなこと思いつくわけもなく…
しかし、気合で数え上げることによってコンテスト中にACできたので解法残します。

解法

 1個以上リンゴが載っている皿のみ、小さいほうから考える。
もし、 1個以上リンゴが載っている皿の枚数とその中で最も少ない個数(=  A _ {min}とする)の積が K以下であれば、 A _ {min}周分 1個ずつリンゴを食べるのでなく、 1周分は A _ {min}個食べるとみなすことで、 1回の操作につき 1枚はリンゴの乗っている皿を減らすことができる。
また、以降の周についても、前の周で一度に減らしたリンゴの個数をゲタとして管理し、ゲタを外すことによって同様に計算ができる。

もし、 A _ {min}と残りの皿の枚数の積が Kより大きい場合は、残りの皿について均等に減らせるリンゴの個数は少なくとも A _ {min}未満のため、 Kと残りの皿の枚数から愚直にシミュレートできる。(具体的には、残りの皿の枚数と Kの商を残りの皿から引き、ハンパはインデックスが小さいほうから 1づつ引けばよい)

そこで、 1個以上リンゴの乗っている皿について、皿のインデックスとリンゴの個数をヒープで管理し、以下のような操作を行う。

  1. ヒープから乗っているリンゴの個数が最小である皿の情報をpopする(=リンゴの個数を A_{i}, 皿のインデックスを iとする)
  2. ゲタを g、残りの皿の枚数を sizeとし、 (A _ {i} - g) * size \le kの場合は A _ {i} 0に更新し、 Kから (A _ {i} - g) * sizeを引き、新たなゲタを A _ {i}に更新する。そうでない場合は、取り出した情報を戻したうえで、操作をやめる。
  3. 操作後になおも K 1以上である場合は、上で述べたとおり、残りの皿の枚数と Kの情報を使って、愚直に Aの値を更新する。

実装上の注意として、 3.の操作時に K 1以上であることと、皿の枚数を 1以上であることを確認しないと、 Kを皿の枚数で割った場合に 0除算起因のREをもらうことがある。(具体的には、 \sum A = Kであればそのようなことが起きうる)

上記解法は、ヒープを用いて操作をシミュレートする箇所がボトルネックになるので、 O(NlogN)程度で動作し、十分高速に動作する。

実装は以下の通り。

Submission #35130846 - TOYOTA MOTOR CORPORATION Programming Contest 2022(AtCoder Beginner Contest 270)