HHKBプログラミングコンテスト2022 Winter(AtCoder Beginner Contest 282) G問題メモ
G - Similar Permutation
問題
- 1~N の順列を2つ作ることを考える
- A=(A1,...,AN)、B=(B1,...,BN)
- i=1~N−1 につき、「右隣の要素が自身より大きいか小さいか」が A と B で一致している箇所の個数を「類似度」とする
- 数式的に言うと、(Ai+1−Ai)(Bi+1−Bi)>0
- 類似度がちょうど K になるような A,B の組の個数を、与えられる素数 P で割ったあまりで求めよ
- 2≤N≤100
解法
1回の遷移に O(N2) かかる4次元DPで、全体で O(N6) かかるところを、2次元累積和で O(N4) に高速化。
挿入DPと言われるらしいが、個人的なイメージでは挿入と言うよりダルマ落としのように抜いていく感覚。
挿入DPと検索して出てくる解説ページを見ても説明の仕方が様々で、これが本当に一緒の種類のDPなの?
となって、この言葉の指す範囲がちゃんと分かっていない。
残っている中で何番目を最後に使ったか
先頭から順番に、順列の要素を A,B 同時に決めていく。
Ai+1,Bi+1 に置く値を決めて類似度がどうなるか(大小が一致して1増えるか、変わらないか)を判断するには、 Ai,Bi に何を置いたかが重要となる。
ただ、具体的な値と言うよりも、「残っている中で何番目」かの情報が重要となる。
(具体的な値だと、i−1 以前に何を使ったかも覚えてないといけないが、それは場合分けが多すぎて扱いきれない)
- 参考: Educational DP Contest T
以下のDPを考える。
- DP(t)[i,j,k]=At,Bt までを決め、A の末尾には残っている中で i 番目に小さい値、B の末尾には j 番目に小さい値を用いて、暫定類似度が k の順列の決め方の個数
t に関しては、DP(t) を求めるには DP(t−1) の情報があれば十分なので、1個前のみ引き継ぐ形で実装上は省略する。
遷移
ある t,k についての更新を考える。配るDPで考えると、
i\j 0 1 2 3 4 5 0 1 2 * 3 4 5
例えば、末尾を含め6個の値が残っていて、i=2,j=3(At は自身より小さい値が2個残り、Bt は3個残っている状態)からの遷移を考えると、
- 類似度が1増えるのは、
- At+1 に At より小さい値を、Bt+1 に Bt より小さい値を置く
- At+1 に At より大きい値を、Bt+1 に Bt より大きい値を置く
類似度が変わらないのはその逆なので、以下のようになる。(+が1増える、0が変わらない)
i\j 0 1 2 3 4 5 0 + + + 0 0 1 + + + 0 0 2 * 3 0 0 0 + + 4 0 0 0 + + 5 0 0 0 + +
ただし、このDPの特性上、今の値を示す行・列は次のDPでは削除されるため、次に引き継ぐ位置は以下のようになる。
i\j 0 1 2 3 4 0 + + + 0 0 1 + + + 0 0 2 0 0 0 + + 3 0 0 0 + + 4 0 0 0 + +
よって、この表を元に
- DP(t+1)[:,:,k+1] のうち、“+“になっている箇所
- DP(t+1)[:,:,k] のうち、”0”になっている箇所
に、DP(t)[i,j,k] を加算してやればよい。
これは、1つ1つに加算していればTLEになってしまうが、更新対象のそれぞれが矩形になっているため、2次元imos法で高速化できる。
ある矩形領域 i=[P,Q) j=[R,S) に一律 a を加算したいとき、、、 R S +-------+ 4箇所に加減算した上で、 P |+a |-a 後から2次元累積和を取ると、実現できる | | | | +-------+ Q -a +a
t=N までやって、DP[0,0,K] が答え。