目次

大和証券プログラミングコンテスト2022 Spring(AtCoder Regular Contest 138) A,B,C,D 問題メモ

Daiwa Securities Co. Ltd. Programming Contest 2022 Spring(AtCoder Regular Contest 138)

1年に何回もスポンサーしてくれるから、なんかSpringとか付き始めちゃったよ。

A - Larger Score

A - Larger Score

問題

解法

初期位置で先頭 $K$ 個に属する要素をまとめて $F$、それより後ろをまとめて $B$ と呼ぶことにする。

スコアを現状から変えるには、何かしら、$F$ から要素を追い出して $B$ の要素を迎え入れないといけない。
この時、2個以上の要素を追い出す必要があるか?というと、ない。

      F    |   B           入れ替える2個が、a+b < c+d ならスコアは上がるが、
...  a  b  |  c  d  ...    それなら、単独でも a < c のように、
          ↓               (a,bのいずれか) < (c,dのいずれか) が成り立つ組み合わせがあることになり、
...  c  d  |  a  b  ...    その単独同士を入れ替えた方が、絶対に操作回数は少ない

$F,B$ から1個ずつ、$B$ 由来の方が大きくなるように要素を選び、この2個の距離を近づける操作だけを行えばよい。
そのときの操作回数は、両者の初期位置の距離に等しい。

なので、$A$ の全要素を、初期位置のindexがわかるように値で降順ソートし、大きい方から調べていく。
この時、同じ値なら、$F$ の方が先になるようにしておく。

「$L$: $B$ に属する要素のうち、暫定調べた中で、最も左にある要素のindex」を更新していくと、

としていけば、$O(N \log{N})$ で答えが求まる。

Python3

B - 01 Generation

B - 01 Generation

問題

解法

操作をシミュレーションして作れる数列を試してみると、「先頭は必ず0」という共通点が見つかる。
たしかに、先頭が1の要素は作りようがない。

また、その先頭の0は、全要素が0でない限りは、Aによって追加されたものということになる。
全要素が0ならBだけで構築可能なので考察終わり。

どうも逆順にたどるとよさそう。
$x=C$ からはじめて、以下の操作を繰り返して、$x$ を空にできたら達成可能、という解法が思いつく。

実際には全て反転させていたらTLEなので、「今、$x$ の中身は反転して扱いますよ」フラグを切り替えて管理する。

これで上手くいくことをちゃんと証明するには、「本当は達成可能なのに、途中で末尾の0を敢えて残さないと失敗する」 パターンが絶対にないということを言わないといけないが、正直そこは曖昧なまま通した。

改めて考えると、操作B'を貪欲に適用した結果、$x$ が失敗したとして、

  失敗時点のx |  削除した文字
1 0 0 1 ... 1 | (0 0 0 1 1 0 0 0)

A'は先頭からしか見ないので、削除した文字を仮に残していたとしても、それが成功可否に与える影響は、ない。
むしろ下手に残すと、本来ならB'で削除できた要素が先頭に来た時、それが1だったため失敗、ということがあるため、切り詰められるならなるべく切り詰めた方がよい。

ので、操作B'を貪欲に適用する逆順操作で大丈夫なことがわかる。

Python3

C - Rotate and Play Game

C - Rotate and Play Game

問題

解法

ローテートしなくても先手は相当有利で、ほぼ、$A_i$ の中で大きい方から半分(上限値)を取れる。

$A_i$ の中で大きい方から半分を「強いカード」として、

のどれか1つでも成り立つ場合は、先手が全て回収しきる前に後手が強いカードを取ってしまうので、上限値を達成できない。

7  1   6  8   3  4   5  2    先手が6,7,8を全て取るのに3手かかるが、
^      ^  ^          ^       後手がそれより早く、そのいずれかを取る状態になってしまう

だが、前半に強いカードが固まっているということは、後半は弱いカードの方が多いということで、 なんとなく、後手に強いカードを1枚も取らせないローテート回数がありそう。

ローテートが済み、順番が決まった後の判定は、正しい括弧列の判定と似ていて、弱いカードを+1、強いカードを-1としたときに、 $2,4,6,...$ 番目の累積和が全て非負なら成功、と言い換えられる。

   7  1   6  8   3  4   5  2
0 -1 +1  -1 -1  +1 +1  -1 +1
0 -1  0  -1 -2  -1  0  -1  0
            ~~ ←だめ

ここで「累積和の中で最低値を取る箇所の次」を先頭に持ってくれば、累積和には一律に(-最低値)がプラスされる。
結果として、累積和の全要素を非負にできる。

   3  4   5  2   7  1   6  8
0 +1 +1  -1 +1  -1 +1  -1 -1
0  1  2   1  2   1  2   1  0

これで先手は大きい方から $K$ 枚を悠々と取れる。

タイブレーク

$A_i$ に同じ要素があって、ちょうど上位・下位半分にまたがって同じ値 $X$ があるとき。
$X$ は強いと見なすべきか、弱いと見なすべきか?

これは、数さえ合わせれば、適当に $A_i=X$ である $i$ の中から強いカードを決めてしまってよい。
どう選んでも、破綻無く上のアルゴリズムを適用できる。

Python3

D - Differ by K bits

D - Differ by K bits

問題

解法

グレイコード、という名前自体は覚えていなかったが、 なんか本問の $K=1$ の時に相当する、bitを1箇所ずつ変えながら $N$ 桁の全ての数を巡回する方法があるのは記憶していた。

その差分となるbit位置は、LSB(Least Significant Bit)を取れば上手くいく。
(これはサンプル1を観察することでも類推できる)

i   Gray  iのLSB
0   0000
       v  0001
1   0001
      v   0010
2   0011
       v  0001
3   0010
     v    0100
4   0110
       v  0001
5   0111
        :

なので今回も、 「LSBが0b0001の時にXORする、$K$ bitが立っている何らかの値」 「LSBが0b0010の時にXORする、$K$ bitが立っている何らかの値」……を事前に決めてやれば、似たようなことができないか。

試してみると、$K$ が偶数の時は不可能とわかる。popcountが偶数の数しか作れない。

また、$N=K \neq 1$ の時も不可能とわかる。0と、全bitが立った数しか作れない。
($N=K=1$ の時は、0 1 で完結するので構築可能)

その他、つまり $K \lt N$ かつ $K$ 奇数の時は、構築可能か?

公式Editorial

1次独立な $N$ 個の「$N$ bit 中 $K$ 個のbitが立った数」を用意できればよい。

を、いずれも空で用意する。両者の要素が $N$ 個になるまで、以下を繰り返す。

要素が $N$ 個になったら成功。

実際にやってみると、$K \lt N$ かつ $K$ 奇数の時は、全て可能であることがわかる。

これを元に、グレイコードよろしく、$0$ からはじめて $F_1,F_2,F_1,F_3,F_1,F_2,...$ という順番でXORを適用していけばよい。

適当に

$N$ bit中 $K$ bitをランダムで選んで立てて、それをローテートしたものを $N$ 通り用意しても、割と高確率で独立になる。

001011
010110
101100
011001
110010
100101

成功するまで最初の $K$ bitの立て方を試しても、まぁ十分な確率で成功する[要出典]。

N=6, K=3,  000111
N=9, K=3,  001001001
N=10 K=5,  0111010001

など、ローテートの過程で重複ができるもの、または0と1を反転したものが重複するものは、独立にならず失敗する。

Python3