Memo

Twitterに書くには長すぎることを書きます。Opinions are my own.

CEOI 2018 Day1 Lottery

CSA

CEOI 2018 Day1 Lottery

https://csacademy.com/contest/ceoi-2018-day-1/task/lottery/

5時間3問ratedコンテストのうちの1問。5時間がっつり粘った結果、この問題に関しては45点→80点と伸び、コンテスト直後にAC。他の2つは歯が立たず、解説の登場待ち。

45点解法 O(N3)

コード: https://csacademy.com/submission/1711355/

  • 愚直にn-l+1個の長さlの区間を示す数字列を用意し、それらを全て比較してどれだけ異なる文字があるかカウント
  • ans[(比較した文字列)][(異なる文字数)]をインクリメント
  • ans[i][j]で、iを固定してjを累積和していく (この工夫がないとO(N4)になるのでおそらく20点解法)
  • ans[i][k]が答え
  • subtask 1 と subtask 2 (n<2000) が解けて、45点

80点解法 O(N2)

コード: https://csacademy.com/submission/1714372/

  • 例えば、1つ目の区間と2つ目の区間で比較すべき文字の組み合わせ((a_1, a_2), (a_2, a_3), ... ,(a_l, a_l+1))と、2つ目の区間と3つ目の区間で比較すべき文字の組み合わせ((a_2, a_3), (a_3, a_4), ... , (a_l+1, a_l+2))がほぼ全て重複していることに着目する
  • ずらしながらカウントしていくことで、O(N2)で45点解法と同様の ans[i][j] が求まる
  • ans[i][k]が答え
  • 追加で subtask 4 と subtask 5 が解けて、80点
  • subtask 3 はMLE

満点解法 O(N2 log q)

コード: https://csacademy.com/submission/1714623/

  • 45点解法や80点解法だと ans[i][j] で (n-l+1 * l+1) のサイズの配列を確保するため、shortで持たせても最悪 2×5000×5000 = 50MB 程度のメモリが必要となる
  • 今回の問題では32MBのメモリ制限のため、nが大きく、lがnの半分に近い値だとMLEする
    • なぜか subtask 3 (q=1, k_1=0) だけが落ちる
  • クエリ数 q が最大でも100しかないことに着目すると、ans[i][j]のうち、j=k_i 以外の部分は不要なので、メモリを節約できる
  • k_iをソートしたものをksとして、ans[i][distance(ks.begin(), lower_bound(ks.begin(), ks.end(), (異なる文字数)))] をインクリメントする
  • 45点解法や80点解法と同様累積和
  • ans[i][distance(ks.begin(), lower_bound(ks.begin(), ks.end(), k_i))] が答え
  • メモリサイズは最悪でも 2×10000×100 = 2MB 程度