目次

AtCoder Grand Contest 050 (Good Bye rng_58 Day 1) A,B,C問題メモ

AtCoder Grand Contest 050 (Good Bye rng_58 Day 1)

A - AtCoder Jumper

A - AtCoder Jumper

問題

解法

制約が $2^{10} \simeq 1000$ なので、二分木的なアプローチを使いそう。

最大数ができたら全てできるので、$N=1000$ を考える。
ただ、1000は大きすぎて考えづらいので、$1 \le M \le 15$ ページを4回までのクリックで巡ることを考える。

ページ1からだけを考えると、二分木のように $i$ から $2i,2i+1$ に辺を張っていくと3回以内で全てに行ける。

       1
     /  \
   2      3
  /\    /\
 4  5  6  7
 /\  /\  /\  /\
89⑩⑪⑫⑬⑭⑮

これをもとに、他の頂点からも上手く行けるようにしたいが……リンクは一方通行なので上へは戻れない。
1~7は全て2リンクを使い果たしているので、葉である8~15を使うしかない。

葉からは全て1にリンクさせてやり直せば?という案が思いつくが、例えば2→15などはほぼ2回分木を辿ることになってしまう。
葉ごとに、ちょうどいい感じの戻り方をしなければならない。

いろいろ試すと、葉からも同様に $2i,2i+1$ に辺を張りつつ $\mod{M}$ を取る(正確には1引いて $M$ で割ったあまりを1足す)と、上手くいく。

               1
         ,----'  `----,
       2              3
     /  \          /  \
   4      5      6      7
  /\    /\    /\    /\
 8  9  ⑩  ⑪  ⑫  ⑬  ⑭  ⑮
 /\  /\  /\  /\  /\  /\  /\  /\ 
123456789⑩⑪⑫⑬⑭⑮1

二分木のように $i$ から $2i,2i+1$ に辺を張った木は、左上から頂点番号が連番になるが、これは $\mod{M}$ を取った後でも成り立ち、$1~M$ がループする形になる。

すると、1から4回で行ける層は、$2^4$ 個の要素が連番で並ぶ。この中には当然 $M$ 以下の値が全て含まれる。

ここで頂点2を根としてみる。階層間では連番でなくなるが、第 $k$ 階層に、$2^k$ 個の数字が連番で並ぶという性質は変わらない。

               2
         ,----'  `----,
       4              5
     /  \          /  \
   8      9      10      11
  /\    /\    /\    /\
 1  2  3  4  5  6  7  8
 /\  /\  /\  /\  /\  /\  /\  /\ 
23456789⑩⑪⑫⑬⑭⑮12

よって二分木のように辺を張れば、第10階層には1024個の数字が連番で並び、その中に $1~N$ が含まれることが保証できる。

Python3

B - Three Coins

B - Three Coins

問題

解法

問題タイトルはショッピングモールで見かける300円均一の雑貨屋さんか。

可能な最終配置

置くことができるコインの位置を考えると、

..ooo..... → ..oooooo.. → ..oo...o..
                         → ..o...oo.. → ..o...ooooo.. → ..o...o...o..

3個続けて置いたコインの一部を、あたかも距離3ずつ移動させるような操作をすることができる。

コインを取り除く操作は、結局この「移動させる」操作と同一視でき、以下の2操作ができると考えてよい。

こういう、いくつかの操作を繰り返してできる配置が可能かどうかが重要な問題では、 移動前後の不変量を見つけることで、 逆にそれを満たすような配置は必ず作れる、ということを利用する解法がよくある。

変わらないものを奥華子ばりに探すと、コインが置かれた座標の $\mod{3}$ を見れば 必ず最初に置くのは $(0,1,2), (1,2,0), (2,0,1)$ のいずれかになり、移動させても変わらないことがわかる。

どれだけ移動させても (2,0,1)

0120120120    0120120120    0120120120120
..ooo..... → ..oo...o..
           → ..o...oo.. → ..o...o...o..

逆に、間が全て空きマスで、このいずれかの順に並んだ3つのコインは、必ず作れる。
置く操作で一緒に置いた3枚のコインを「3つ組」と呼ぶことにする。

もう一段階考察を進めると、3つ組を移動させて開いた穴に、 また同様に3つ組を置き、移動させることができる。
そうすると、必ずしも最終配置において3つ組($(0,1,2)$ など)が繰り返されるわけでは無くなる。

  2            01        2 120        01        2 1   20     01
..o............oo.. → ..o.ooo........oo.. → ..o.o...oo.....oo..

ただし、

なら、構成可能であることがわかるし、これが満たされないなら無理。

区間DP

括弧列のように、先頭から $0 \mod{3}$ の位置を取ったら「あと $1,2$ が1個ずつ必要」みたいに開く数と閉じる数を管理するDP、というのは筋が悪そう。
0,1,2を開いた個数が同じでも順番によって閉じ方が変わってくるので、同一視してまとめられる状態数がそこまで小さくならない。

以下のような区間DPをするとよい。

そうすると、$[l,r)$ 内の3つ組としてあり得る3マス $(i,j,k)$(これは $\mod{3}$ でなく本来の座標で)について、

の合計が、$(i,j,k)$ が3つ組だった時に $[l,r)$ から得られる得点となる。
全ての $(i,j,k)$ について求めた最大値が $DP[l][r]$ となる。
区間の狭い方から埋めていくことで、計算できそう。

しかし、単純にこれを埋めていこうとすると、$O(N^5)$ のループとなってしまう。

$l \le i \lt j \lt k \lt r$ かつ $\mod{3}$ 上の制約があるにしても、ちょっと無理。

ここで、DPを以下のように更新すると、効率よく計算できる。

こうすると、$O(N^3)$ に収まる。

区間全体が新たな3つ組の両端にならないなら、必ず事前に計算した2区間をくっつけた形で表現できる、というのがなるほどポイント。

Python3

C - Block Game

C - Block Game

問題

解法

両者の行動方針

刑事は行動可能範囲をなるべく狭めたい。
最初の1個はともかく、あとは、より怪盗のいる方の空きマスが少なくなるように置いていけばよさそう。(ちゃんとした証明はわからん)

■・・・・・・怪・・・・・・・・・■

■・・・・・・怪■・・・・・・・・■  B

■・・・・怪←←■・・・・・・・・■  SS

■・・・■怪・・■・・・・・・・・■  B

■・・・■→怪・■・・・・・・・・■  S

■・・・■・怪■■・・・・・・・・■  B

すると、怪盗の最適な行動は、壁からなるべく離れた位置を保つこととなる。

移動可能範囲のちょうど真ん中までたどり着ける('S'がそれだけ続く)ならそこへ
■・・・・・・怪■  B

■・・・怪←←←■  SSSSSS

足りなければ'S'が続くだけ壁から離れる
■・・・・・・怪■  B

■・・・・怪←←■  SS

その後、刑事は怪盗の真横へまたブロックを置くので、怪盗の次の移動可能範囲(怪盗のいるマス自身を除く)は、以下の小さい方となる。

これが繰り返され、終了までに移動可能範囲が0になったら刑事の勝ち。

逆に怪盗が勝つには、以下のように'B'に挟まれた'S'の個数が、後ろから $1,2,4,8,16,...$ 以上ならよい。

B SSS...SS B SSS...SS B SSS...SS B SSSS B SS B S B
      32        16         8         4     2   1

どれか1箇所でも、連続する'S'の個数がこれ未満ならアウト。

なので、$s$ から'?'を書き換えたときにこういう条件を満たす文字列がどれだけ作れますか、という問題を解けば、それを $2^Q$ から引けば答えとなる。

DP

上記のようにかなり刑事に有利なルールであり、むしろ怪盗が逃げ切れるケースの方がまれなので、そちらを数える。

前から決めていくと、あと何回'B'が出てくるかによって'S'が直近に連続してほしい数が異なってきてややこしいので、後ろから決めていきたい。

$j$ の上限は21くらいでよい。
'B'が1回出てきたら $2^1+1$、2回出てきたら $2^2+2$、$k$ 回出てきたら $2^k+k$ 文字が最低必要となるので、$k=20$ で $10^6$ を超える。

遷移を配るDPで考える。後ろから $i$ 文字目($i=0,1,2,...$)が

'B'が次にどこに出てくるかは、最初に'B'の出てくる位置を求めておけばすぐに求められる。

また、$i+1+2^j$ が $|s|$ を超える場合は、便宜上 $|s|$ に切りそろえる。

これで、計算量は $O(|s| \log{|s|})$ となる。

Python3

怪盗は自分の手番で必ず動かなくてはならない、だったら、偶奇によってちょうど中央で止まることができないことがあるので、もう少しややこしかったかも知れない。