geam1113’s diary

主にAtcoderのコンテストの備忘録を書きます。AtCoder緑と水色を行ったり来たりしています。

AtCoder Beginner Contest 261 参加記録

コンテスト中AC:A〜F

D - Flipping and Bonus

説明のため、お金をスコアコイントスによって表か裏かを選んでスコアを得ることを操作と呼びます。

その時々の操作後のスコアを最大化するような貪欲法は使えません。
そこで、操作回数とカウンタの最大値がN\leq 3000であり、小さいことから、DPできないか考えます。

dp[i][j]:=i回目の操作終了後にカウンタがjの時のスコアの最大値

このDPテーブルの大きさ(状態数)はN^2 \leq 9\times 10^6となり十分小さいです。

次にdp[i][j]への遷移を考えますが、その前にカウンタで得られるスコアについて、次のようにしておきます。

カウンタがxの時のスコアをD_xとする。なお、xC_1,\ \cdots C_Mに含まれていない場合は、D_x=0とする。

  • j\geq 1
    i-1回目終了後にカウンタがj-1の状態であって、i回目にカウンタを+1する操作(コイントスが表)となった場合に遷移します。この時、スコアとしてX_iD_{j}が加算されます。
    dp[i][j] \leftarrow dp[i-1][j-1]+X_i + D_j

  • j=0
    カウンタが0の場合は、i回目でカウンタを0にする操作をした時です。i-1回目のカウンタの状態に関わらず遷移するので、i-1回目のスコアの最大値となります。
    dp[i][j] \leftarrow max(dp[i-1][0],\ dp[i-1][1],\ \cdots ,\ dp[i-1][N])

j=0の時にN回、1\leq j \leq Nの時に各1回ずつの計算が必要となるため、i=1,2,\cdots ,Nと更新していった時、各iについて、2N-1回の計算が必要となります。従って、遷移にかかる計算量はO(N^2)です。
(1\leq j \leq Nの計算の際にmax値を同時に計算していけば、定数倍軽くなると予想されます。)

よって、計算量も問題なく、DPで解けることがわかりました。
Submission #33455701 - AtCoder Beginner Contest 261

E - Many Operations

ビット演算の問題は、2進数の各桁毎に考えるのが典型です。

非負整数Sの2進数表記のd桁目をS^dと表すことにします。

非負整数Xに対して操作1から操作3までを実行した場合を例に考えます。
操作1,2,3はそれぞれand,\ or,\ xorであったします。また、求めたい値をY_3とします。
つまり、

Y_3 = ((X\ and\ A_1)\ or\ A_2)\ xor\ A_3

です。
ここで、d桁目のみに着目しても以下が成り立ちます。

Y_3^d = ((X^d\ and\ A_1^d)\ or\ A_2^d)\ xor\ A_3^d

2進数の桁は01しかありません。
そこで、

B_3^d = ((0\ and\ A_1^d)\ or\ A_2^d)\ xor\ A_3^d
C_3^d = ((1\ and\ A_1^d)\ or\ A_2^d)\ xor\ A_3^d

という風に予め計算しておくと、X^dが0か1かを判定することで、O(1)Y_3^dが求められます。
各桁について同様に求めることで、Y_3を構築できます。0\leq d \lt 30という制約があるため、高々30回の計算で済みます。

また、B_3^d,\ C_3^dB_2^d,\ C_2^dから以下の様に求められます。

B_3^d = B_2^d\ xor\ A_3^d
C_3^d = C_2^d\ xor\ A_3^d

これは操作3に限らず、一般のiについて、i-1の値からiの値を求めることができます。

実装では、2進数表記で、00...011...1から始めて、順にA_iとビット演算していきました。
このようにすれば、実装は少しシンプルになるのかなと思います。
詳細は実装を参照ください。
Submission #33463754 - AtCoder Beginner Contest 261

F - Sorting Color Balls

隣合う要素を入れ替えてソートするのはバブルソートです。バブルソートの最小交換回数は反転数に等しいです。そこで、反転数を軸にこの問題を考えます。

まず、色がない場合を考えます。
iより前にあって、X_iより大きいものは自分より後ろに来なければいけないので、必ずX_iと入れ替えなければいけません。
よって、その数をc_iとすると、少なくともc_1+c_2+\cdots +c_Nのコストがかかります。
これがコストの下界となります。コンテスト中は具体的な証明が思い浮かばなかったのですが、バブルソートの最小交換回数と反転数が等しいことから、実際にこれを達成できるはずと考えました。

次に、この考え方に従って、色がある場合を考えます。
iより前にあって、X_iより大きいものでも、色が等しいものは交換にかかるコストが0なので、無視できそうです。
そこで、iより前にあって、X_iより大きいものであり、かつ、色が異なるものの個数をd_iとすると、少なくともd_1+d_2+\cdots +d_Nのコストがかかると予想できます。

これが色を考慮した場合のコストの下界となります。具体的な証明はコンテスト中は浮かばなかったのですが、色が異なる場合と同様の操作をして、同色の分のコストを無視すれば良いだけなので、同様にこれが達成できるだろうと考えました。

結果として、この考え方は正しかったです。
おそらくこの証明は、Web解説youtube解説で示されているのかなと思います。

後は反転数の計算ですが、各色に対し、自分と異なる色のみの反転数を求めるのは難しいと考えました。
そこで、全体の反転数を求めた後、各色の反転数を引くことで求めました。
全体の反転数はFenwick Treeで求めました。
各色の反転数については、全色分のFenwick Treeの領域を確保するのが難しそうだったので、色ごとにX_iを分類した後、分割統治法で求めました。

実装はコンテスト後に修正したものです。
Submission #33567025 - AtCoder Beginner Contest 261