CODEFESTIVAL2018 Final G:Chicks and Cages

解法

 dpっぽい雰囲気はあるが、何の整理もなしにはうまくいかない。まず、ある鳥かごに振り分けられたひよこの集合をXとする。この鳥かごで発生するストレスの値は|X|\sum_{i \in X} A_iである。つまりストレスの値の総和は、ひよこの大きさに、そのひよこが入っている鳥かごのひよこの数を係数としてかけた値の総和ということになる。これがわかると、小さいひよこにできるだけ大きい係数を、大きいひよこにはできるだけ小さい係数をかけたくなる。実際、2つのある鳥かごに振り分けられたひよこの集合をX,Yとし、|X| \lt |Y|とする。このとき、i\in X,j\in YかつA_i \gt A_jがなりたつ(i,j)の組があるとき、ひよこiとひよこjを入れかえることで、ストレスの総和を小さくできる。このことは、A_iの小さい順にひよこを鳥かごに振り分けることで得られる最適解が存在することを意味する。
 ここまでくると、A_iを昇順にsortして、dp_{i,j}:i番目のひよこをj個の鳥かごを用いて振り分けたときの最小値 というdpができそうという気分になる。更新式は、A_iの累積和をS_iとして

  • dp_{i,j} = \min(dp_{i-k,j-1}+k(S_i-S_{i-k}))  (1\leq k \leq i)

である。しかしこれではO(MN^2)となり間に合わない。この遷移を高速化できないだろうか?
 「大きいひよこにはできるだけ小さい係数をかけたい」という気持ちを思い出そう。この気持ちに立ち返ると、iが大きくなるにしたがって、調べるべきkは小さくなっていくのではないか?という予想が立つ。実際、sortした後であれば、j番目の鳥かごのに入ってるひよこの数をk_jとすると、最適解においてはk_j \geq k_{j+1}が成り立たなければならない。これを勘案すると、dp_{i,j}を更新する際には、kの値としてi/jまで考えれば十分であることがわかる(k_1=k_2=\cdots=k_jみたいな状況がk_jが最大となるときに考えられる振り分け方だから)。従って更新式は

  • dp_{i,j} = \min(dp_{i-k,j-1}+k(S_i-S_{i-k})  (1\leq k \leq i/j)

となる。このときの計算量は、計算量に調和級数が現れる形になるのでO(MN\log N)であり、十分間に合う。

実装

Submission #5713037 - CODE FESTIVAL 2018 Final
dpそのものは基本的だと思う。