/ 最近 .rdf 追記 設定 本棚

脳log[2020-10-12~]



2020年10月12日 (月) [AtCoder] Twitter での企業コンの話題。自分も最初は企業コンは対象外だと思ってスルーしていた。■知らなかったこと1。企業コンに ABC 級と ARC 級があること。社長さんのツイッターで知りましたよ。■知らなかったこと2。ARC と企業コン(の難しい方)が別物だということ。最近 ARC 開催の土壌が整ったとかで立て続けに開かれたけど、自分が AtCoder に参加して1年以上も ARC が途絶えていたから、ARC は終了した名前で、企業コン(の難しい方)として存続しているのだと理解していた。ABC 級の企業コンがあることも知らなかったし。■知らなかったこと3。問題の難しさを A~F ではなく配点で予測すること。配点が(見積もりとはいえ)絶対尺度だということ。AtCoder をはじめたての頃は恐ろしいことに、AGC の A 問題と ABC の A 問題を区別する基準を持っていなかった。「A 問題よりは難しい B 問題」とか「たかだか B 問題に一日散々苦しんでおいて」とかの感想がそれを反映している。700点問題と500点問題なんだよなあ……(最近は400点問題も解けない)。


2020年10月07日 (水) [AtCoder]「【お知らせ】AtCoderの問題文が表示されない不具合が報告されておりますが、ブラウザのバージョンを最新のものにアップデートすることで、正常に表示されるかと思います。 なお、Internet Explorerはサポート対象外となっております。ご了承ください。」■うん、見えない。atcoder.jp と cloudflare.com のスクリプトだけを許可してたけど、他の、google-analytics.com 以外のスクリプトを許可しても見えない。インストールできる最新版が Firefox ESR 52.9.0 なんだけど、これでは見えない。CSS を切ったら見えたけど、数式が読みにくくなる。■今の段階でエラーになるスクリプトは次の1か所だけで、ページ埋め込みスクリプトの <script>$(function(){$('var').each(function(){var html=$(this).html().replaceAll('<sub>','_{').replaceAll('</sub>','}');$(this).html('\\('+html+'\\)');});});</script> が「replaceAll is not a function.」というエラーになっている。これ1か所だけに対応するコードは String.prototype.replaceAll = function(s,r){ return this.split(s).join(r); }; とか、グローバルフラグを付けた正規表現パターンを第1引数にして String.prototype.replace を replaceAll の代わりに呼び出すとか。■atcoder.jp から送られてくるページの埋め込みスクリプトを書き換える方法がわからなかったので、replaceAll 関数を事前にブラウザに定義することにする(userChrome.js)。しょうもないもんに引っかかってるなあと思うけど、おかげで対策が簡単で助かる。今はまだ?■……と思ったら、「@chokudai「さすがにこの対応状況で入れるのは時期尚早でしょ、ってことでちょっと修正コミット入ったっぽい caniuse.com/?search=replac…


2020年10月03日 (土) 平均寿命と平均余命。■社会科か家庭科か忘れたけど、学校で習ったときは結局よく理解できなかった。教科書には平均寿命とは0歳時点での平均余命である、みたいな定義が書いてあるんだけど、そのことの意味が腑に落ちなかった。今でもうっかりするとあやしい。■どういうことか。今年の発表で日本人の平均寿命が86歳(仮)ですよ、みたいなことが言われるとする。60歳の人が、じゃああと26年は生きられそうだなと考えるのは、概ね間違いではないけど、期待に一方的な偏りがある。現在60歳の人は60年生き延びてきた実績を持つ選ばれし者なので、平均して26年以上生きることが期待される。それが60歳の平均余命であり、0歳の平均余命である平均寿命との違い。たぶんね。■しかし、平均寿命の根拠となるデータは過去のデータであって、生まれたばかりの赤ん坊の寿命はこれからの環境に左右される。平均寿命が当てになる根拠って何だろう。大きな変動がない? トレンドを加味した予想(外挿)?■平均寿命は86歳(仮)ですよ、86歳の平均余命は何年ですよ、というのが最も当てになる数字ではなかろうか。ま、こんな素人考えの及ばないところまで保険会社の人は考えていることだろう。儲けに関わるからな。■平均寿命のイメージって、人口を年齢別に分けて、それぞれに平均余命を足して平均したものって感じ。実際どうなんだろう。


2020年09月30日 (水)

最終更新: 2021-01-19T03:44+0900

[Ruby] 平衡二分探索木

 前置き

最近こういう記事を読んだ(20200912p01)。「【Python】平衡二分木が必要な時に代わりに何とかするテク【競プロ】 - Qiita

その少し前に雰囲気で書こうとしたけど、バランスの取り方に対する理解が雑で完成しなかった(20200604p01.04)。

Ruby の標準添付ライブラリにある SortedSet は内部構造がハッシュテーブルでありキーの順序付けが利用できない。ありていに言えばキーの二分探索をしたいができない。「RBTree ライブラリ (https://rubygems.org/gems/rbtree) が利用可能である場合、内部記憶としてハッシュの代わりに RBTree を使用します 」ということが書いてあるけど、RBTree が利用可能だったことがない。

 再挑戦した。sset.rb (3.8 KiB)

  • SortedSet と食い違いが見つかるまで無限ループさせてみたけど、限られた範囲のキーと限られた時間では止まらなくなった(こういうときはテストの失敗を疑う。だから最初に失敗するテストを書いて、それを満足させるように実装を埋める)。
  • 性能はまったく期待できない。まったく。

    注意すれば省メモリにはなるかもしれないけど、出し入れのたびに配列の全長のおよそ半分を右へ左へ動かしていたのでは、他に何も期待できない。

    注意を要するのは rotate_l/rotate_r の実装。このとき(20200905p01.07)のように、不必要に膨大なメモリ要求が実行速度まで低下させかねない。

    すばらしき(20200912p01.03) Array#fill メソッドにならって、Array#rotate も第2引数以降を使って対象範囲を受け付けたらいい。

  • 内部構造は「社長から始めて決まったやり方で社員を一列に並べていったら、ある社員とその部下と部下の部下以下末端までを一定の連続する範囲で表せるのではないかと考えた。なんのことはないそれって深さ優先探索と同じ順番だったのだけど」(20200607p01.02)と書いたときのものと同じ。

    その後の検索で「最小共通祖先 [いかたこのたこつぼ]」というページを見つけて、「LCA」「オイラーツアー」という用語を仕入れている。そんな感じの構造。

    ちょっと待て、このドメイン名は……。20200905p01.03 で参照した AtCoder の ikatakos さんと同じでは?

  • 苦労の 70 % くらいは sink と push の2メソッドを見出すところにあった気がする。実装することではなくシグニチャを発見するまでのところに。でも、どういう操作が必要か、どういう操作であれば十分か、実装を始めてデバッグをする過程でしか見つけられないジレンマ。

    以前にも似たようなことを書いている。「メソッド名を決めるまでで 9割が終わってる。」 そのときはその後の検索で「最小全域木」「プリム法」「クラスカル法」という用語を仕入れてクラスカル法で再実装しているが、今回はどうか。AVL木とか赤黒木とか知らないよ?>「平衡二分探索木 - Wikipedia

  • key の順序(SSet#index(key))と内部配列の添字の変換に何か魔法がないものか。ありそうではないか。
    • 最大値と最小値を蓄えている内部配列における位置は計算で求まることがわかった。
    • ソート列における順序と内部配列における添字という、2つの数字を元にして each メソッドが簡略化できそうな気がする。したい。

      つまり、現在の向き(行きか帰りか)と次の添字がわかるならスタックがいらなくなる。開始点(最小値の添字)はもうわかっている。

あっ、これ二分探索のためにあらかじめ並べ替えたソート済み配列だ(いま気がついた)。Array#bsearch_index と Array#insert で済むものをよくも難しく書き直したものだ。

メモリブロックの移動を減らすためにギチギチに詰め込まないでルーズに管理しようとしたら、固定長の大きさを持っていて最大値と最小値で特徴付けられる疎な配列(リスト)の入れ子構造に行き当たって、ピボットはいらないな、そうするとこれ木じゃないな、ただの(入れ子になった)ソート列になっちゃったな、と。じゃあ原点に戻ってあれも(並べ方が素直じゃないだけの)ソート列だな、と気がついた次第。

持っていることも忘れていた『[単行本] K. メールホルン, P. サンダース【アルゴリズムとデータ構造―基礎のツールボックス】 シュプリンガー・ジャパン株式会社』をぱらぱらめくってると、(a,b)-木という構造があって、これは木の各ノードが最長で長さ b の子ノード列を持つらしくて、つまりは入れ子になったソート列なんだけど……。

入れ子になったソート済み配列もやっぱり木?


2020年09月26日 (土) 小学校で行われるテストに2種類あって、先生が回収して後日採点済みのプリントを返却するものと、自己採点方式でテストを受けた時間の後半に全員が自身の答案を採点するもの。自分ははっきりと自己採点方式の方が好きだった。解答と結果発表が日をまたいでしまうと、テストの内容に再び興味を持つことが難しい。問題と答えのつながりがはっきり見えるという点で算数が好きだったのと根っこは同じ。■AtCoder のようなオンラインジャッジ方式の学習面での利点がどこかで語られていたけど、小学校での経験に照らしてもそうだったなあと思ったのだった。


2020年09月22日 (火)

最終更新: 2020-10-14T18:33+0900

[AtCoder] ACL Contest 1A 問題 Reachable Towns

ACL は ARC と AGC の中間あたりの位置づけだそうな。この A 問題は 300 点問題。1問目のこれしか解けそうにない。

 最初の提出 #16962327 (TLE)

移動可能な範囲が第Ⅰ象限と第Ⅲ象限に限られるが、移動先の点からさらに移動先を選ぶことができる。双方向に移動可能だし、X と Y の比率を変えながらジグザグに Y=-X 方向に移動することもできる。ともあれこの感じ(「友達の友達は友達」)は UnionFind だと思った。

問題は Union する点の選び方で、見境なく Union したら TLE になった。

見境なくとは言っても、相互に移動可能なら片方向だけを取り扱えば足りるわけで、X 座標の昇順に処理することで X 座標の大きい方から小さい方だけを見るようにしている。X 座標のソートに関してもこの問題で NlogN の時間をかけるのはもったいなくて、線形時間でソート列が手に入る。

 3番目の提出 #16962544 (AC / 565 Byte / 559 ms)

Union した中で一番条件のいいものだけ代表として残すようにしたら AC。

 Python で一番速い提出 #16928237 (maspy さん / 363 ms)

よくわからないが UnionFind ではない。キーは12行目の if x + min_y == N+1: だと思う。UnionFind で形作られるグループが持つ幾何学的性質が何かあるのだろうか。

右上がりの対角線上に並ぶ場合と右下がりの対角線上に並ぶ場合を対極として、その中間の状態がうまく考えられない。

X 座標と Y 座標がともに 1..N の順列だということから導かれる論理的必然性を何か見落としてると思う。


検索してたら答えらしきものが見えちゃったんだよな、maspy さんのページは避けてたんだけど他の所で。

頂点をソートして x 座標が小さい順に見ます。

頂点 i と頂点 i+1 について、「y1, …, yi が (N,…, N−i+1) の順列」であるときのみ非連結であり、そうでないとき必ず連結になることがわかります(あとで証明かなんかできたらいいな、、)

わかります」(わかりません)

 提出 #17013139 (AC / 1055 Byte / 326 ms)

maspy さんの提出に沿って*理解したことをひとつひとつコメントにしながら書いた。完全にそのままではなく、「# ymin の最初期化が必要?」とコメントしたように、ループ中の代入をひとつ省略した。(あ、タイポ。最初期化→再初期化)

しかし、ガイドなしの独力でこの道筋が見つけられるとは思わん。

 「UnionFind で形作られるグループが持つ幾何学的性質」

けんちょん(敬称略)のページにわかりやすい図があった。へー、そうだったのか(まだ見えていなかった)。

ACL Contest 1 A - Reachable Towns (300 点) - けんちょんの競プロ精進記録

でも図を見てみたらある意味わかって当然の図ではあった(それがわからなかった)。つまり、x + y = N+1 という X と Y の関係式を見て幾何学的性質について考えたのだし、であれば、その性質は y = -x + N+1 という直線に関わるものでしかありえない。答えを目の前にしながら「わからんなあ」と悩むふりをしていたのだった。下手の考え……。

* ~に沿って、というのはある意味嘘。こちらにゴールがある、という指針だけを手にして考えた結果の式が一致することを確かめただけ。結果が同じなのだから考えたことの軌跡をコメントとして残さなければ完全丸コピと見分けがつかない。コメントを書くのは必然だった。


2020年09月20日 (日)

最終更新: 2020-10-10T17:39+0900

[AtCoder] AtCoder Beginner Contest 179/D,E,F

 D 問題 Leaping Tak

コンテスト時間は D 問題で詰まっているうちに終わってしまった。計算過程で余りをとらないと一部の入力で TLE になってしまうのだが、余りがうまく計算できなかった。何を言っているのか自分でもよく解らない。

 提出 #16922727 (AC / 302 Byte / 282 ms / 15976 KB)

一日経ってみれば普通に AC できた。どこに詰まっていたのか解らない。

もうちょっと書く。方針。

  1. スタート地点1に立って移動可能なポイント(S の要素)を眺める。
  2. スタート地点から(1ステップで)それらの地点へ行く方法は1通り。メモする。
  3. 地点1に一番近い移動可能地点A(1とメモしてあるはず)に移動する。
  4. そこから移動可能なポイントを眺める。移動した分だけスライドしたが地点1からの眺めと同じである。
  5. 地点Aに来る方法が1通りなので、地点Aから行ける地点へ行く方法も1通り増える。メモする。
  6. 以下、3、4、5の繰り返し。
  7. さて、地点 N に来る方法は何通りになったでしょうか。

移動可能地点ひとつひとつに対してメモしていては TLE になる>提出 #16879797

S の要素数は N に準ずるが、S を定義する区間の数は幸いにも最大で 10 に制約されている。メモの仕方を工夫して、絶対値ではなく変化量を記録する。どうせ地点を端から端まで処理するつもりなので、変化量をつどつど加算していけば絶対値は得られる。これでループ1回の書き込み量が区間の両端の数(最大で20)まで減る>提出 #16883620。TLE なのは、途中で余りを求めなかったから多倍長整数の桁数に比例した計算量に押し負けた結果。

ここから途中過程で余りをうまく求められなかった(冒頭に戻る)。

 E 問題 Sequence Sum

D 問題で詰まったのでコンテスト中に問題文は読まなかった。色気を出せば解ける D 問題も解けなくなるので。

 提出 #16923150 (AC / 253 Byte / 68 ms / 15728 KB)

すんなり書き下してデバッグの必要もなかった。N の値が膨大だが M の値がそれなりなので、余りの種類もそれなり。となれば A 数列は途中から循環する。

 F 問題 Simplified Reversi

D 問題で詰まったのでコンテスト中に問題文は読まなかった。色気を出せば解ける D 問題も解けなくなるので。

 提出 #16923745 (AC / 518 Byte / 513 ms / 17472 KB)

すんなり書き下してデバッグの必要もなかった。縦軸横軸それぞれにブロックラインが単調に前進していくだけなので、それを BIT に記録した。

@chokudai「F問題とても好きな問題なんだけど、データ構造でいくらでも殴れちゃうのが残念……O(N)で解いてみてね><

BIT は読み書きともに対数時間なので、さっきの提出は O(NlogN) になる。O(N) で解くというチャレンジ課題がまだある!

 提出 #16937624 (AC / 269 Byte / 254 ms / 17392 KB)

N に比例したループの中で長さ N(-1) の配列に書き込むとしても、書き込む要素の総数が 2N 以下にとどまるなら O(N) なんじゃないかな。かな?

ひと工夫しないと配列への書き込み量が N×N になってしまう罠がある。変数 ii を介在させて書き込みタイミングを一拍遅らせたことが書き込み量削減のキモ。これには以前日記で触れた「Scintilla 方式」が参考になった。その要諦は……「メインのデータ構造はギャップバッファ。そこに張る行インデックスの更新コストの問題。更新が必要なインデックスのエリアはある点から始まり必ず末尾で終わる。ある点をひとつ記憶しておくことで更新範囲をある点とある点の差分にすることができる。

 ところで kotatsugame さんが…… 提出 #16893528 (121 Byte / 198 ms)

ゴルフをしながら Ruby の中で最速タイムを記録していたのだった。異次元過ぎてさっぱりわかりません。

 @2020-10-09 提出 #17269548 (AC / 249 Byte / 243 ms / 17464 KB)

お風呂でなんとなし思い付いた。

メインループのイテレーションごとに X 軸と Y 軸が参照軸と更新軸のどちらかの役割を受け持つ。参照軸更新軸それぞれが N-2 要素のメモを持つ。メインループの中で……

  1. 参照軸のメモを参照したい要素まで(必要なら)埋める。
  2. 参照した値でスコアを更新する。
  3. 更新軸のメモを参照した値の範囲まで(必要なら)埋める。

前回の提出では更新軸のメモが更新の対象だったが、今度の提出では更新の一部が参照軸の参照時に分散している。その結果ループの中がストレートになり、値の大小関係によってあっちの値を参照したりこっちの値を参照したりという場合分けが不要になっている。しかし変わらないタイム差(たぶん配列参照のコストが大きい)。

メインループの中に2つの対称的な書き込みループがあるあたりが kotatsugame さんの提出と共通だと思ったけど、あちらでは一回に片方のループしか実行していなかった。たぶん参照軸のメモだけ更新しているのではないか。もはやこの軸の命名が意味不明であるが……。

更新軸が更新軸である所以はそれが「ブロックライン」をメモする軸であり、今後のスコア(何枚の黒石を裏返せるか)に影響するから必要があって書き込むからなんだけど、何枚の黒石を裏返すことができるかを知るために見る参照軸のメモだけが更新の対象でいいなんて、どうして想像できる?

参照軸のメモは「何枚裏返すことができたか」の記録として捉え直すことができるんだろうな、きっと。それだけわかれば十分だということの理解はまだ。

 提出 #17272161 (AC / 205 Byte / 244 ms / 17380 KB)

更新軸の更新部分に当たる1行を削除してみたが AC のまま。たしかに参照軸のメモだけ更新していれば十分みたいだ。

「ある座標より後ろは何枚裏返せたかの記録がまだない」というのが、参照軸のメモから読み取るべきもうひとつの情報であり、これは変数 ii の意味とほぼ同じ。だから十分。


2020年09月18日 (金) あんちべ「SQL、めっちゃ書ける自信があります(ドヤァ」 同僚A「SQLで素数を列挙して欲しいんですが」 同僚B「SQLでマンデルブロ集合を描けたな確か」 あんちべ「SQL、何もわからない…」」■SQLite3 で考えてみた。create table P (N integer primary key); create view NP as with recursive NI(N) as (select * from (values(2) union select N+1 from P order by N+1 desc limit 1) union select NI.N+1 from NI where exists (select * from P where P.N*P.N <= NI.N and NI.N % P.N == 0)) select * from NI order by N desc limit 1; 素数を記録する表 P と、表 P を参照しながら次の素数を返すビュー NP を定義した。このように使う。insert into P select * from NP; 効率は知らない。SQL っぽく無限集合から素数が湧いてくるように定義できれば、limit, offset で自由に素数が取り出せるのだけど……。自然数の無限集合ならできる(with recursive N(N) as (values(1) union all select N+1 from N) select * from N;)。でも素数は?■マンデルブロ集合を描く SQL は想像もつきません。■あっ、SQLite のページにあった!「The following query computes an approximation of the Mandelbrot Set and outputs the result as ASCII-art」 このページからは全体に、「SQL はチューリング完全!」とか言って嬉しくなってしまうような人の気配を感じる。だめだよ野放しにしては(いいぞもっとやれ)。


2020年09月17日 (木) [WR250R] 5年目2万キロを超えたところでバッテリーが死んだ。セルの回転が弱いなと思っているうちに、数日おくとセルが回らなくなり、すぐにその間隔が1日になり、とうとうどれだけ走ってもセルを一回も回せなくなった。エンジンを止めるたび押しがけである。それも一発でかからなくなっていった。■AZ の LiFePO4 バッテリーにした。純正採用の GS ユアサ(の鉛蓄電池)よりさらに高価。だが軽い。冬季の始動性と寿命がどうなるか。それにより次のバッテリーが決まる。


2020年09月16日 (水) 少し前の新聞から3選。■「周りに迷惑をかけまいというのは、何でも自分で決めたいという一種のエゴ。生きているかぎり人は誰かに頼るほかない。だから、うまく迷惑をかけあう能力を磨くほうが先」 お坊さんの言葉なので、これで気を楽にして生きやすくなる人もいるのだろうな。あるいは戒めとしても。■「役人が恐れるのは、人事の影響を受けるのは自分だけではないと思うからです。直属の上司、その上の上司、部下、ひいてはトップの事務次官、大臣らの人事にも響く、と感じています。私もふるさと納税の時、総務省のある先輩から『君だけの問題じゃ済まなくなるからな』と言われました」 どうして忖度してしまうのか、そんなに我が身がかわいいか、と思っていたのだけど、こういう考えもあるのかと初めて知った。自分はこういう風には考えられないし行動を自制できないので、想像できなかった。■偶然にも同じ日の新聞から。「女性はもう一つ、被害を訴えにくい理由に「チームに迷惑がかかること」を挙げる。「みんなソフトボールで日本一になりたくて大学に来ていた。だから、自分さえ黙っていれば、と」」 ここでも。連帯責任は人を縛るのに有効なのだなあ。


2020年09月15日 (火) 高木浩光@自宅(テレワークを除く)の日記 - テレフォンバンキングからのリバースブルートフォースによる暗証番号漏えいについて三井住友銀行に聞いた」■組織として全体にこの銀行のレベルは相当高いよね。これ以上はセキュリティ一辺倒でなく現実的な経営判断になるんじゃないの? (過去の)判断の結果として現状がある、惰性で流れていない、という印象を受けたということ。


2020年09月14日 (月) 「見えないものは存在しない」についての面白い比較。■「「見えないものはない」についての考察 | 村中直人の雑記帳」「「見えないものはない」についての考察②ASDとADHDの比較 | 村中直人の雑記帳」■自分の場合。人間とはそのように振る舞うものだという学習の結果としてこの言葉を使っている>20200811。人間を代表する主要なサンプルはもちろん自分自身なんだけど、対象を広くとれば、隠せば見つけられなくなる人がいる、という当たり前のこととして言っている。■注意力と意識とリマインダの話。■たとえば、手が3本必要な状況が発生したとして、片方の手に持っている物を手近な椅子に置くとする。まず間違いなくそれはそのまま置きっぱなしで忘れられてしまう。そういう自分自身との付き合いももうたいがい長いので、今では手を離そうとした瞬間に「あ、これ忘れるやつだ」と気がついて持ち直したり、忘れても大丈夫なように面倒でも定位置に置いて手を空けるようにする(手に持っていた鍵がないとあとで気がついても、鍵の置き場所を探せば見つかる)。常に一部の注意を残しておくような器用なことができない。■たとえば、出かけるときに「あれ買ってきて」と頼まれるとする。必ず紙に書くなどして目に触れるようにしなければ忘れる。必ず忘れる。頼まれたという事実も、何を頼まれたのかも覚えられるけど、帰るまでにやらなければいけない頼まれ事がある、ということが意識に上らない。で、紙に書いた用事をフロントガラスの内側に置いたりします。嫌でも目に入るだろうという考えで。ときどきはその紙に注意が向かないまままっすぐ帰ってきたりするんだけど、不可抗力だからしかたないよね。■「見えないものは存在しないし、目の前にあるからといって見えているとは限らない」■そんなんだからミスの原因は注意力の欠如ではなく必ずシステムや構造、手続きの中に求める。注意をしていれば、ぼんやりしていなければミスをしないはずだ、なんて考えることはできない。それは注意していなければミスをするということだし、自分は必ずミスをするということだから。■これってクリティカルな場面では普通の考えで、プレス機を動かすのに両手が必要とか、(最近では蔑ろにされてるみたいだけど)給油口を開けるのにエンジンキーが必要とか。どんな人間でもバイオリズムの変動はあるわけで、なまじ身体能力が高くて普段苦にしないより常にこういう考えができる方が役に立つと思う(両方持てる人が一番だけど!)。


2020年09月12日 (土)

最終更新: 2020-10-09T18:36+0900

[AtCoder] AtCoder Beginner Contest 128E 問題 Roadwork

精進ですよ。今日*こういうものを読んだ。

【Python】平衡二分木が必要な時に代わりに何とかするテク【競プロ】 - Qiita

この前の日記(20200907p01)で散々 TLE に苦しめられた問題も、C++ なら変数 r を map に、変数 nmin を multiset にすることで、ある範囲のキーを二分探索で検索することも、小さい方からキーを取り出すことも、STL 任せで妥当な時間で行える。適当に速くて短い提出を選んだけどこんな感じ>「提出 #16578878」。トリックは必要ない。

Qiita の記事で題材にされているのが今日の E 問題 Roadwork で、記事をよく理解するためにまず解きたいと思った。

 提出 #16613595 (TLE×1 / AC×14)

とってもくやしい。

最悪の場合に 200k 要素の配列に 200k 回書き込みを行うのが良くないのかなと思う。掛けて 40G×単位サイズの書き込み量。あ、これはやべーわ。

遅延更新と区間更新が可能なセグメントツリーがあればこのアホな書き込み量はなんとかなる気がするなあ。

 提出 #16618964 (AC / 721 ms)

ループの中でがっつり二分探索して配列のスプライシングをしても通るあたり、この前の F 問題(前掲)より易しい。

二分探索がしたい、線形よりましな時間で挿入がしたい、というときに平衡二分探索木が欲しくなるんだよね。

プライオリティキューを実装するときも、最大(最小)値を得るだけでなく、整列済みのキューにアクセスして操作したいときがある。でも内部構造がヒープだからできない。std::multiset とは違う。

2本目のキューに削除済みとマークした要素を入れておくの頭いい(Qiita の記事)。二分探索はできないけど、一度放り込んだ値を後から取り消したいときが、たしかに以前あった。

 Ruby によるすべての提出 (実行時間昇順)

Ruby のバージョンが違うので一概に比較できないけど、他の AC はどれもヒープを使用していて 1000 ms 以上かかっているところが共通している。配列のスプライスよりヒープの方が賢いよね。

でもスクリプトで手の込んだことをするよりインタープリタに丸投げした方が速いこともある。Python は汎用スクリプト言語でありながらそういうバッチファイル的、グルー言語的なあり方も板についている。

たとえば、ダイクストラ法、ワーシャルフロイド法などのアルゴリズムが名前で利用できる。ヒープ構造もある。二分探索も、比較式をブロックで与えられる汎用性が Ruby にはあるが、それが遅さに繋がってしまう。実は lower_bound, upper_bound だけでほぼ足りる。オブジェクトの形が不定だとしても、key 配列と value 配列を持てば解決する。

Ruby に範囲を指定する Array#fill があるのを、しかも古くからあるのを知ったときは嬉しかったし、同時に自分の不明も明らかになった。Ruby は汎用スクリプト言語だからループやイテレータを使って Array#fill 相当の処理は自在に書ける。書いてきた。でも書かずに fill を1回だけ呼び出すのが賢い(が、実は Ruby で実装されています、という可能性もなくはない)。

* 日記を書いている今日は10日。