/ 最近 .rdf 追記 編集 設定 本棚

log[20190922] AtCoder Grand Contest 038B問題 Sorting a Segment



20190922()

最終更: 2020-05-06T23:25+0900

[AtCoder] AtCoder Grand Contest 038B問題 Sorting a Segment

A 問題よりは難しい B 問題C, D, E, F は一目見て問題文の理解を諦めたよね

 最初の提出 (WA/TLE/AC)

TLE(時間切れ)は潜在的な AC だという期待が持てるからっきり WA (Wrong Answer)だと告げられる方が問題AC と混在しているあたり微妙なケースの考慮が漏れている

問題にあたる方針はこう長さ Kドウを数列 P に重ねて1ずつスラドしながらドウの中の要素をソトするとする

スラドに伴いドウからはみ出た要素が直前のドウの中で最小(最大)の要素であったかどうかまた新しくドウに入った要素が現在のドウの中で最大(最小)の要素であるかどうこの2点に注目するだけで全体の数列の並びに変化があったかどうかがわかる

ただしこれだけでは足りない

 2番目の提出 (WA/TLE/AC)

pm という変数によりドウ内の要素が最初からソト済みだった場合の考慮を試みているだがまだ整理し切れておらずACWA になったケースや逆に WAAC になったケースがある

 3番目の提出 (TLE)

WA がなくなりACTLE のみにちなみにこの時点でコンテトはとうに終了している

 4番目の提出 (TLE)

答えが得られる main 関数が確定しているのであとは TLE を解消すべく意味を変えないように注意しながら効率の良い実装に置き換えるリファクタリングに励むだ

……なんだけど3番目より効率が落ちて TLE が増えたしかし頭の中を整理する役には立ったドウ内の要素をソトしない手法への転換もこのときこれが5番目の気づきにつながった

 5番目の提出 (AC / 427 ms / 30132 KB)

ドウの中で最大(最小)の要素かどうかを判定するのに先日の20190907p01.03のデータ構造が使えると気づきNext メソドと Gen メソドをコピペして利用したところ AC

ドウ内の要素の並びが最初からソト済みかどうかの判定には右方向に単調増加の要素がいくつ続くかというデータを利用したこれを作成するループはやはり先日「小細工としてのデータ LXRX を作成したものと同じ構造をとっている

 6番目の提出 (AC / 329 ms / 42816 KB)

同じく先日の20190907p01.06で使ったインデックスの作成方法とソト方法を利用してタイムを改善した

係数がいくつでも O(N) だとはいえ長さ N のフルスキャンを4回も5回もやり長さ N のデータ配列を1011も作成すればオーバーヘドはいかばかり一部の入力に対しては初期の提出に比べて3倍の時間をかけているしメモリ使用量は倍以上

他からの流用そのままではなくこの問題に最適化した手法であるとか根本から別物の優れた手法であるとかがないものNo Idea なんだけども

 7番目の提出 (AC / 295 ms / 36544 KB)

主として NextIndex メソドの無駄と NextIndex メソドの変数名の間違いを修正したリファインっと速くなってちっと省メモリだがまあ小細工

 Ruby による提出一覧

 Ruby で一番新しい提出>https://atcoder.jp/contests/agc038/submissions/7648285

WA が2つある以外は ACタイムもメモリ使用量も優れている

cnt_up, cnt_k は自分の提出における UP に相当するものだけど、min_deq, max_deq を中心としたその他の大部分はっと見当が付かないくらい違っていて面白いどういう着想を持つとこういうコドになるんだろう

ドウをスラドするものとして扱うのではなく両端の要素に着目してドウを分類しカウトしているのだろうこのあたり(ドウの処理順)適当な制限条件を付けて最適化が可能な雰囲気がなきにしもあらず。

 他の提出を見てると多いのは

最大値最小値それぞれについて待ち行列を用意するものみたいRuby で一番新しい提出もそうポイトは

  • 待ち行列の要素数は K 以下
  • 追加する要素との大小関係によって待ち行列の末尾から永遠に最大要素(最小要素)としての順番が来ない要素を追い出す。

8番目の提出 (AC / 243 ms / 19124 KB)

いいね時間もメモリ使用量「7番目からさらに改善した

気がついてる提出を見なかったけど(※主に見たのは PythonRuby とは提出数が桁違いなんだよなあ)最小値の方の待ち行列の長さを見れば最初から昇順にソト済みだったかどうかがわかる

Queue から追い出す処理に while 文が使われてるけどそこと Array#shift に目をつむると()N 回のループ1つで終わり余分なメモリ要求も計 2×K 要素の配列だ

※キー1つあたり push がループ全体で N 回なのでpop/shift を合わせた回数も全部で N 回以下にとどまるArray#shift がどうしても気になるならメモリ要求を 2×K でなく 2×N にすれば定数時間にできる

9番目の提出 (AC / 243 ms / 18428 KB)

Array#shift を定数時間の処理に置き換えようとしたら追加のメモリ要求が 2×N になるどころか N だけになったが2<=K<=N なので 2×KN の大小関係は一概には言えない(※最悪の場合がマシだとは言える)しかも Ruby では速度の改善にはつながらず……

ところでC配列のト操作とRubyArray#shift の実装が別物なのは言うまでもないあくまでも原理的な話であってタダで手に入るオールマイはないのだから気にして損はないだろうということRuby 1.9array.c を見たら内部ポインタのインクリメトで済ませているようだったので得することもなかったみたい(そうunshift はダメだけど shift は気易く使っていいの)