(AOJ1635) ICPC2019 国内予選-D:計数カウンタ

想定解は結構難しいと思ってしまった。

解法

 「ある区間を選び、その区間の数字を同時に1増やす」という操作が1回であるとどういうことが起きるかを考えてみる。例えば、3つのカウンタがあって、すべてa_i\lt b_iが成り立っていても、両端の2つはたくさん押す必要があるが、真ん中の1つはほとんど押さなくてよいという状況を考えてみる。このとき、両端の2つを別々に押すよりも、3つ同時に押すことを何回か行った方が得をするということがあり得る。このとき真ん中のカウンタではMから1に戻るということが何回か起きるが、これは目標となるb_iの値を、整数jを用いてb_i+jMに置き換えることにしよう。解くべき問題は、「各カウンタiについてjをうまく選んだとき、カウンタiの値をa_iからb_i+jMにするには最小で何回の操作が必要か?」というものである。jの値としては、カウンタの数であるNまで考えれば十分である。
 ところで、1番目のカウンタが最適解において何回押されるかということはすぐにわかる(カウンタが1つのときを考えればよいから)。1番目のカウンタの状態が決まれば、1番目のカウンタのことは考えなくてよいので、その状態において2番目のカウンタを何回押すべきかも計算できる。このように考えてみると、端からdpをしたくなってくる。dp_{i,j}:「i番目のカウンタまで見て、i番目のカウンタの値をb_i+jMにするときの操作の回数の最小値」としよう。
 dp_{i,j}への遷移を考えよう。基本的には、カウンタi-1とカウンタiの、各状態における押すべき回数の差分を足し合わせていくことになる。このとき、カウンタi-1の方が押すべき回数が多いということがあり得るが、その場合はカウンタi-1を「先に押しておいた」と解釈し、ノーコストでカウンタiのときの値に遷移することで解決する。カウンタib_i+jMにするためには、そのカウンタを(b_i-a_i)+jM回押す必要がある(j=0かつb_i\lt a_iの場合は除く)。この値をc(i,j)としよう。dp_{i-1,k}からdp_{i,j}への遷移は

  • dp_{i,j} = \min(dp_{i,j},dp_{i-1,k}+c(i,j)-c(i-1,k)) \ (c(i-1,k)\lt c(i,j))
  • dp_{i,j} = \min(dp_{i,j},dp_{i-1,k}) \ (c(i-1,k)\geq c(i,j))

となる。これでは遷移がO(N)かかり、計算量は全体でO(N^3)である。ICPC国内予選なら多少待てばよい(僕のチームはこれで通した)が、AOJでは通らない。
 遷移を高速化しよう。上の遷移のほとんどは「無駄」である、つまり不必要な計算であることがわかる。例えば、j+1 \lt kであるkでは、常にc(i,j) \lt c(i-1,k)が成り立つ。つまり、dp_{i,j}への遷移はjの大きい方からj+2までの累積minを取っておけば、O(1)で行うことができる。k\lt jの方の遷移はどうだろうか。c(i,j)-c(i-1,k)を書き下すとc(i,j)-c(i-1,k) = (b_i-b_{i-1})-(a_i-a_{i-1})+(j-k)Mであり、前半2項の最小値は-2(M-1)であることを考えると、k\lt j-2では、jに遷移する際にカウンタiを単独でM回以上押すことになる。しかし、カウンタiを単独でM回押すなら、カウンタi-1をさらにM回押して損がない。つまり、k \lt j-2であるkからjへの遷移は、k=j-2,j-1からjへの遷移に勝ることがない。従って、そのようなkからの遷移は計算する必要がないことがわかった。
 以上のことより、遷移として計算すべきはk=j-2,j-1,j,j+1j+1\lt kだけで、これはそれぞれO(1)で可能であった。よって、dpの遷移を無事O(1)にすることができた。全体の計算量はO(N^2)となり、これならAOJでも間に合う。

実装

AIZU ONLINE JUDGE: Code Review
実装は標準的。

ちゃんとやると考察難易度が高いと思う。想定解で通した通したところはすごいなあ...。