AtCoder Beginner Contest #118

published: ,
last modified: .

lang: ja

category: competitive_programming

tags: atcoder ocaml

A. B+/-A

一瞬約数判断を素因数分解か結果の埋め込みかで解こうとしてしまった.editorialにもあるがppqqの約数かどうかはqqppで割り切れるか,すなわちqmodp=0q \bmod p = 0かで見ることができる. 解答例

B. Foods Loved by Everyone

配列で個数をカウントするよくある手法. 解答例

種類iiの出現回数,すなわちiiを好きな食べ物としている人数をa[i]a[i]に記録しておけば,種類iiが全員に好かれているかはa[i]=Na[i]=Nか否かで判断可能.記録するためにはiiが見つかるたびにa[i]a[i]+=11をすればよい.

C. Monsters Battle Royale

直観的に通してしまった.恐らくCodeforcesで類題を見たことがある.解法としてはgcd{Ai}\gcd\{A_i\}を取ればよい. 解答例

editorialが親切なのであまり書くことがないが,答えの候補にgcdがあることの雑な説明:攻撃による体力変動から最後のモンスターの体力はkiAi\sum k _i A_iの形になるはず.これは整数kik_iを上手に決めて作れる数の範囲内であるから,その粒度は最も細かくてgcd{Ai}\gcd \{A_i\}である(実現可能性を扨措いて).これにより求める答えがgcd以上であることが言えたから,あとはeditorial通りユークリッドの互除法に気付けばgcdが実際に構築可能かつ最小と分かり,答えとなる.

ユークリッドの互除法になることについてもちょっと書いておきたくなった:体力p,q s.t. p>qp,q \text{ s.t. } p \gt qについて大小関係が逆転するまで(p,q)(pq,q)(p,q) \to (p-q,q) ...\to ... (pkq,q)\to (p-kq,q)と遷移させると確かにpkq=pmodqp-kq = p \bmod q

D. Match Matching

10410^410001000だと思い込み,コードテストで1200ms(C++で400ms)で動くのに何で提出するとTLEなんだろうとか言っていた. 解答例

最初に浮かんだ方針はdp[i][j]dp[i][j] \coloneqqii桁目まで使って残りマッチ数がjjのときに作れる最大の整数(int64を超えるため文字列で管理)」[1]. 更新は

dp[i][jcost[k]]max{dp[i][jcost[k]],dp[i1][j]^k}dp[i][j-cost[k]] \gets \max \{ dp[i][j-cost[k]], dp[i-1][j] \char`\^ k\}

である(kkは使える数字,cost[k]cost[k]はそのコスト,「^\char`\^」は文字列連結)が,TLEする[2]

ここで桁数iiが要らない気がしてくる:例えば使う数字として11(コスト22)と77(コスト33)を考えてもかなり隙間が空くことになるし,「残りマッチ数がjjのときに作れる整数」を考えるには不要な情報である.そこでさらに状態を纏めてdp[j]:=dp[j] :=「残りマッチ数がjjのときに作れる最大の整数」とし,dp[j]dp[j]を順に埋めていくことを考えると,遷移は

dp[jcost[k]]max{dp[jcost[k]],dp[j]^k}dp[j-cost[k]] \gets \max \{ dp[j-cost[k]], dp[j] \char`\^ k \}

となる.これなら間に合う.

なお文字列で表した数字のmaxは,単にPervasives.maxでは分からない(ほかの言語でも多分そう):maxで使われている文字列比較は辞書順による比較なので1112では後者のほうが大きくなってしまう.したがって(1)桁数の大きさ,(2)先頭から見て初めて相違となる数字の大きさ,の順に決める必要があり,これは自前で実装することになる.

実装に際してPervasives.compareの結果が何を表すかよく覚えていなかったのでメモ:第一引数が大きい(+1)か小さい(-1)か等しい(0)かで覚えるのがよいだろうか.

editorialでは桁数を先にdpで求めてから数字を復元している.正直なところこの問題ではこの方針を採る利点があまり分からない.いちいち文字列を構築したくないときには良いのかもしれない.

因みに「優先度が最高のものを貪欲に並べて桁数最大にする → 余りを調整」みたいな方針も最初の実装中に考えていた.でも2つ以上の数字を使ったりもするので結局似たようなdpをすることになりそう?


  1. editorialの「マッチがjj本のときに作れる最大桁数~~」と紛らわしいが違う.この記事ではマッチの本数nnから始めて残りがjj本のときを,editorialでは最初からマッチがjj本のときを意味している.従って私の実装ではjjnnから00まで降順に埋めていくことになる. ↩︎

  2. 最大桁数がN/2N/2である上に文字列の比較も最悪N/2N/2ステップ……だが均すとlogになるようなならないような(未確認).仮にlogだったとしても少なくともO(N2MlogN)O(N^2 M \log N)はあり厳しそう.実際間に合わない. ↩︎