Processing math: 100%

AtCoder Beginner Contest 314 G問題メモ

AtCoder Beginner Contest 314

言語アップデート後のRated1発目。

ACL for Pythonなどを使えるようになり、また使える問題も出題されたものの、意外と使っている人は少なかった?
既に、より高機能 or 使い方を熟知してる自前ライブラリを持っている場合は、敢えて使う意味は薄いか。

G - Amulets

問題

  • N 体のモンスターがいる
    • i 番目のモンスターは攻撃力 Ai、タイプ Bi
  • M 個のお守りがあり、1M の番号が付いている
  • 体力 H の高橋君が、お守りをいくつかを持って、体力の続く限り i=1,2,...,N の順番にモンスターに対峙する
  • モンスター i と対峙したとき、
    • タイプ Bi と一致する番号のお守りを持っている?
      • 持っている→攻撃を受けない
      • 持っていない→攻撃を受けて高橋君の体力が Ai だけ減る
    • その後、高橋君の体力が
      • 0より大きい→モンスター i を倒す
      • 0以下→モンスター i を倒せず終了する1)
  • K=0,1,...,M について、お守りを K 種類持って出発したときに倒せるモンスターの最大数を求めよ
  • 1MN3×105
  • 1H,Ai109

解法

いろいろ要素があって、何を固定すべきか迷う。

i を固定して考えてみる。
タイプ(お守り番号)ごとに、それまでに出てきたモンスターの攻撃力を集計する。

   i         1      2      3      4      5
(Ai,Bi) = (10,3) (20,5) (10,1) (20,3) (20,5)

タイプ         2 4   1    3  5
攻撃力の和     0 0  10   30 40

この時、お守りを K 個持っていると、攻撃力の和の、大きい方から K 個分の攻撃を防ぐことができる。

逆に言うと、小さい方から累積和を取っていって、k 個目で累積和が H 以上となったら、 i 体目のモンスターまで倒すには Mk+1 個のお守りが必要ということになる。

タイプ         2 4   1    3  5
攻撃力の和     0 0  10   30 40
累積和         0 0  10   40 80
                           ↑H=70 だったら i=5体目まで倒すには、1個以上のお守りが必要
                         ↑H=40   だったら i=5体目まで倒すには、2個以上のお守りが必要

(i1)(i1)K について、答えは i1 となる。
(実際は、ki1 の時と i の時で異なっても1個なので、ある i1 が答えとなる K はたかだか1個となる(全て倒せる場合を除き))

H の位置の探索と、状態(タイプ毎の攻撃力の累積和)の更新を効率的に行えるデータ構造は複数あるが、たとえば累積和の二分探索が可能なFenwickTreeを使って実装できる。

  • 値と個数を別々に管理する必要があるため、2本の木を持つ
  • 「攻撃力の和」としてあり得る値で座標圧縮しておく
    • あり得る値は、事前に実際にシミュレーションして求める。最大 N+1

モンスター i での更新は、 更新前のタイプ Bi の攻撃力の和を S として、S を示す位置から S を引き、S+Ai を示す箇所に S+Ai を足す、 という2回の O(logN) の操作により行える。(個数も同様に、-1,+1で更新できる)

(A6,B6) = (20, 1)  更新前のタイプ1の攻撃力の和S=10
                    -                            +
FWT上のindex     0  1  2  3  4          0  1  2  3  4
示す攻撃力の和   0 10 20 30 40    →    0 10 20 30 40
FWT_値           0 10  0 30 40          0  0  0 60 40
FWT_個数         2  1  0  1  1          2  0  0  2  1

その後、H で二分探索して必要お守り数を求めるのも O(logN) なので、全部で O(NlogN) で求められる。

攻撃力の和が被るとき

2つ以上のタイプで攻撃力の和が被るとき、かつちょうどそこが H の境界になった時、 必要お守り数の計算に注意が必要となる。

FWT上のindex     0  1  2  3  4
示す攻撃力の和   0 10 20 30 40
FWT_値           0  0  0 60 40
FWT_個数         2  0  0  2  1
                         ↑H=40 のとき、FWT_値 上の二分探索で求まるindexは3

2つ以上のタイプが重なっている場合、その中のいくつかを倒せて、いくつかを倒せない、という事態が発生しうる。

累積和が H 以上になる最初のindex(上例では i=3)を二分探索により求めたとき、

  • i1 までのモンスターの攻撃力の和 v・個数 c を求める(v=0,c=2
  • i1 までのタイプを倒した後の残り体力は Hv=400=40
  • i=3 の示す攻撃力 Si=30
  • この時、追加で倒せるモンスタータイプは (Hv1)/S=39/30=1 種類(切り捨て)
  • よって、この時に倒せるタイプは c+(Hv1)/S=2+1=3 種類→M3=2 種類のお守りが必要

このように計算する必要がある。

Python3

1)
ざんねん!! たかはしくんの ぼうけんは ここで おわってしまった!!
programming_algorithm/contest_history/atcoder/2023/0813_abc314.txt · 最終更新: 2023/08/12 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0