HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)F,G問題メモ

F - Black Jack

問題

  • 整数 $N,L,D$ が与えられる
  • あなたとディーラーがゲームをする
    • 各自、持ち点 $0$ からスタート
    • 「$1~D$ の目が等確率で出るダイスを振って、出た目を持ち点に加算する」ことを好きなだけ繰り返す
    • $N$ 点を超えたらバーストで負け
    • 互いにバーストしていない場合、最終的な持ち点の多い方が勝ち
    • 引き分け(互いにバーストした場合や、持ち点が同点の場合)はあなたの負け
  • ディーラーの行動は決まっていて、持ち点 $L$ 点未満の間ダイスを振り続け、$L$ 点以上になったらやめる
    • あなたはディーラーの最終的な持ち点は知り得ないが、この行動基準は知っている
  • 最適に行動した場合のあなたの勝率を求めよ
  • $1 \le L \le N \le 2 \times 10^5$

解法

自分が、バーストの可能性のある持ち点から続けて振るべきかどうかは、 ディーラーの最終的な持ち点の確率分布が分からないと何とも言えない。

ディーラーの行動は決まっていて、持ち点 $i$ になる確率 $P_i$ は計算できる。
なのでまずそれを計算する。

$i=0~L-1$ に対して、ダイスの目の分だけ分配していけばいい。

N=5 D=3, L=4
                                 |→Burst
 i   0    1    2    3    4    5  |  6    7
     1    0    0    0    0    0  |  0    0
     `----v----v----v
       + 1/3  1/3  1/3
     0   1/3  1/3  1/3   0    0     0    0
          `----v----v----v
           +  1/9  1/9  1/9
     0    0   4/9  4/9  1/9   0     0    0
               `----v----v----v
                 + 4/27 4/27 4/27
     0    0    0  16/27 7/27 4/27   0    0
                    `----v----v-----v
                     + 16/81 16/81 16/81
Pi   0    0    0    0  37/81 28/81 16/81 0

以下を求める。

  • $W_i$: あなたが持ち点 $i$ で操作を終えたときの勝率
  • $S_i$: 操作を終えるかは関係なく、持ち点 $i$ からの勝率

$W_i$ に関して、あなたは持ち点でディーラーを真に上回らないといけないので、 $P_i$ の累積和を取って1つ右にずらしたものが暫定の $W_i$ となる。

                                 |→Burstにつき無意味
 i   0    1    2    3    4    5  |(   6     7   )
Wi   0    0    0    0    0  37/81|( 65/81 81/81 )

ただし、ディーラーがバーストした時は、あなたはバーストしてない限り勝つ。
ディーラーのバースト確率 $\frac{16}{81}$ を $W_i$ 全体に加算する。これが最終的な $W_i$ となる。

 i   0     1     2     3     4     5   (  6    7    8  ...)
Wi 16/81 16/81 16/81 16/81 16/81 53/81 (  0    0    0  ...)

あなたは、持ち点 $i$ の時、 「操作をここで終えるか、ダイスを振って持ち点を $i+1~i+D$ のどれかにするか」を選べる。
勝率の高い行動を採用すべきである。

  • $i$ で操作を終えたときの勝率 $W_i$
  • $i$ からダイスを振った場合の勝率 $\displaystyle T_i = \frac{S_{i+1}+S_{i+2}+...+S_{i+D}}{D}$
    • ただし $j \gt N$(バースト)のとき、$S_j=0$

$S_i=\max(W_i,T_i)$ となる。

$S$ をセグメント木などに載せ、$i$ の降順に $S_i$ を求めていき、$S_0$ が答え。

計算量は $O(N\log{N})$。少し実装が泥臭くなるが累積和を使うと、$O(N)$

Python3

G - Retroactive Range Chmax

問題

  • 長さ $N$ の数列、はじめは $A=(A_1,A_2,...,A_N)$
  • $Q$ 個のクエリに答える
    • タイプ1:「1 l r x」で与えられ、$i=l,...,r$ に対して、$A_i$ を $\max(A_i,x)$ で置き換える
    • タイプ2:「2 i」で与えられ、$i$ 番目のクエリを取り消す($i$ 番目は必ずタイプ1のクエリ)
    • タイプ3:「3 i」で与えられ、$A_i$ を出力する
  • $1 \le N,Q \le 2 \times 10^5$

解法

クエリの取り消しが必要。

取り消す更新が加算など逆演算の存在する操作なら、「操作を取り消す=加算した分を減算する」が成り立つが、残念ながら max は操作後から操作前を復元できない。

縦に時間(クエリ番号)、横に $A$ のindexをもった2次元座標をイメージする。

  →index
↓
ク   ┌─┐
エ   │┌┼─┐
リ   └┼┘  │
       └──┘

クエリを先読みして、各クエリがいつ取り消されるかを求めておけば、ひとつのクエリの影響範囲は矩形になる。
この問題は「矩形 chmax、1点取得」のデータ構造があればよい。

双対2次元セグメント木がその条件を満たす。
$Q \times N$ の配列は持てないので必要な部分だけを作る動的セグメント木にすると、空間計算量は $O(Q \log{N} \log{Q})$ で済む。

時間計算量も $O(N+Q\log{N}\log{Q})$ となり、制限時間が長いので、通る。

Python3

programming_algorithm/contest_history/atcoder/2024/0224_abc342.txt · 最終更新: 2024/02/26 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0