目次

AtCoder Beginner Contest 210 D,F問題メモ

AtCoder Beginner Contest 210

やっぱ最近のD問題難しい。

D - National Railway

D - National Railway

問題

解法

$(i_1,j_1)~(i_2,j_2)$ のマンハッタン距離を $M(i_1,j_1,i_2,j_2)$ とする。

マス $(i_1,j_1)$ を固定した時、どっちの方向にもう1つの $(i_2,j_2)$ を取るか。
これは、右下と左下だけ考えればよい。(右上とかは、もう一方を固定したときに考慮される)

右下に固定する。

そうすると、どのマスを $(i_2,j_2)$ にすればよいかは、近いマスでは大体似通ってくる。

[1][7] 7  9   [1]にとっても[7]にとっても[9]にとっても、
[9] 6 <3> 7   そこを(i1,j1)とした時の最適なペアは<3>
 7  8  6  4

これは以下のようにすると見つけられる。

あらかじめ「$C \times M(0,0,i,j)$」を足し込んでおく。

C=2
 1  9 11 15
11 10  9 15
11 14 14 14

右下からの累積Minを取る。これを $B_{i,j}$ とする。

 1  9  9 14
 9  9  9 14
11 14 14 14

すると $B_{i,j}$ は、$(i,j)$ より右下のマスの どこかを $(i_2,j_2)$ としたときの以下の最小値となる。

なので、$(i_1,j_1)$ を固定したら、そのときの最適な答えは以下の小さい方となる。

選ぶ2マスは相異なってないといけないが、$B_{i,j}$ は $(i,j)$ を選んだときも含まれているので、 それを除くために $B_{i_1,j_1+1}$ と$B_{i_1+1,j_1}$ をそれぞれ参照する。

全ての $(i_1,j_1)$ を試して、その中の最小が(ペアが右下にある場合の)答え。

あとは左下にペアがある場合も考慮するため、 $A_{i,j}$ を左右反転(とか、90度回転とか)した結果で同じことをもう1回やって、小さい方が答え。

何回か $H \times W$ 行列を行ったり来たりするが、1回1回は $O(HW)$ なので、十分間に合う。

Python3

F - Coprime Solitaire

F - Coprime Solitaire

問題

解法

最近難しめの典型でもABCで出してくるので、2-SATも来たか、という感じ。
まぁこれに関してはAtCoder Library Practice Contestにあったしね。

2-SATっぽいとは思ったけど計算量の減らし方がわからなかった。

2-SATとは、以下のような問題を解くアルゴリズム。

今回のように、カードの裏表を決定するような問題設定などで解法となる場合がある。

中身は、論理関係を有向グラフで表して、強連結成分分解によって矛盾しないか調べている。
詳しい解説や類題は以下。

素直な2-SAT

2-SATソルバは、ライブラリとして準備しておけば、(実装にもよるが)使用する際は以下の3つの手順となる。

今回の場合、$i$ 枚目のカードの上に向ける面を $A_i$ にするときTrue、$B_i$ にするときFalseと扱って、 $N$ 個の変数の状態を決めるように2-SATソルバを使う。

各 $A_i,B_i$ を素因数分解して、例えば $A_i$ と $A_j$ が同じ素因数を持つ場合、この2つは同時に選べない。
こういう関係を全て調べてソルバに登録していけば解ける。

しかし、関係性1つ毎に内部のグラフでは2辺が張られるため、 この関係を素直に2-SATに登録していくと、 意地悪なケースで全ての $A_i,B_i$ が同じ素因数を持っていた場合、 $O(N^2)$ 本の辺が張られることになってしまう。ちょっと間に合わない。

集約する頂点を追加

例えば $i$ 枚目のカードで素因数“2”が使われていて、 他で使われていてはいけないという情報をソルバに与えたいとする。

「$1~i-1$ 枚目のカードのどこかで素因数2を含む面が使われている」ことを表す頂点を追加する。

その上で、「同時に選んではいけない関係性」を矢印で表すと以下の図のようになる。 (ごちゃごちゃするので素因数3,5に関する辺は省略)

「$i$ 枚目のカードを上にした面は $A_i$(True) or $B_i$(False)である」を表す変数を $C_i$ とする。
「素因数 $p$ を $i$ 枚目のカードより前に使った/使ってない」を表す変数を $X_{p,i}$ とする。

禁止される関係性は、以下の3種類になる。

こうすると、全ての頂点間に関係性の辺を張らなくても、O(素因数の種類数 $\times N$)の頂点数で済む。

頂点・辺の効率化

ただし、素数は $A_i,B_i \le 2 \times 10^6$ の範囲で約 $1.5 \times 10^5$ あるので、 満遍なく全て出てきてしまうとやっぱりきつい。

よって、もう少し頂点と辺を減らす。

最低限必要な $X_{p,i}$ は、素因数 $p$ ごとに、 $p$ をどちらか(または両方)の面に書かれたカードが $k$ 枚あったとして、$k-1$ 個必要になる。

この必要数だけ作るように実装を頑張ると、間に合う。

$A_i,B_i$ のどちらかが素因数 $p$ を持つ毎に $X_{p,i}$ が1つ必要になるが、 200万以下の整数が持てる素因数の種類数はせいぜい6個で、両面でも12個。

$C_i$ とあわせてだいたい $13 \times N$ 個の頂点と、 その2~3倍程度の辺が必要になるだけなので、間に合う。

Python3