/ 最近 .rdf 追記 設定 本棚

脳log[2022-02-20~]



2022年02月20日 (日) [AtCoder] 今日は ABC240 があった。見たことのある問題が多かったかな(万年 ABC rated だからだよ)。■C 問題「Jumping Takahashi」。アルゴリズムと数学 演習問題集の 96 問目「Cooking」が ABC204-D と同じ問題で、すでに ABC で解いていたけどそのときの解法が思い出せなかったのでビット演算で解き直していた>提出 #29359695 (AC)。それが4日前。今日の提出もビット演算で>提出 #29519763 (AC)。気がついたけど、ほんのちょっと前の ABC-D が ABC-C として出たってことなのね。■D 問題「Strange Balls」。ぷよだと思ったけど連鎖がなくてちょっと物足りない。必要な情報をメモしてシミュレートするだけ>提出 #29524639 (AC)。■E 問題「Ranges on Tree」。問題文が難しい。3割くらい理解を投げ出しかかっていたけど、途中でもうオイラーツアーが見えていたのでそれをガイドにして(予想がついている内容が外れていないことだけを確認して)読み通すことができた。昨日の F 問題「Construct Highway」がコンテスト終了後とはいえ解けていて気力が充実しているのでなければ危なかった。適当なタイミングで数字をインクリメントするだけ>提出 #29531766 (AC)。こういう問題をもっぱらスタックを陽に持ったループで解くのは 20 万回の再帰呼び出しがローカルではスタックオーバーフローを起こしてデバッグも答え合わせもできなくなるから。ネットで読んだ方法が効かないのはなんでなの?■F 問題「Sum Sum Max」。加速度と速度と距離。それは3か月前に解いたこれ「ARC077-E「guruguru」(黄 diff)。それほど難しくはないかな。ABC-D+α という感じ。プラスアルファで悉くつまずいて AC に辿り着けないのが自分ではあるが。(略)。時間軸に沿って加速度と加速度の累積としての速度と速度の累積としての移動距離を記録して答えにする」。極大値が見えていなかったりとりあえず代入しておいた初期値を答えにしてしまったり、逆に開始点を答えの候補から漏らしてしまったり、答えを合わせるのに大変苦労した。「プラスアルファで悉くつまずいて AC に辿り着けない」とか「区間の左右端と節約回数の上下端が off-by-one エラーを誘発しやすい感じ(ドツボにはまった経験から書いています)」を今日も再現しておかしくはなかった。早々に愚直解とランダム入力を組み合わせてダメケースの発見に努めることができたのが運命の分かれ目。終了3分前の提出 #29545375 (AC)。これ水 diff なんだって。全く同じではないにしてもかつての黄 diff が形無しですよ。


2022年02月19日 (土) よーわからんが、unsignedの2数の平均だったら、2で割ったものを足すだけでいいんじゃないの? 1ビット分誤差が入るからダメって?」 / Twitter」■これ文脈が二分探索なのでは。統計処理ではなくて。整数型なのには意味がある。このトピックが定期的に取り上げられるのはオーバーフローが原因となって言語(ライブラリ)が修正される事例がある上に、知らなければ最初に書いてしまう式だからだと思う。誰もが通る道。1ビットの誤差について、仮に二分探索で左が 3 で右が 5 のときピボットが 3/2+5/2 == 3 になってしまうと 4 がテストされる前に探索が終了してしまう。それは誤り。2数が1の場合でも UINT_MAX の場合でも誤差が1なら「1ビット分誤差が入るからダメって?」に対するツッコミにならないのでは?という疑問には、それはそうと思う。誤差が大きすぎるという納得はわからない。


2022年02月18日 (金) 3000ミリ秒近くかかっている。 なお Perl で同じコードを実行してみたところ、5ミリ秒で終わった。 Ruby では 22 秒(ミリ秒ではない!)かかった。 正規表現の実装が、ぜんぜん違いそうである。」 / Twitter」■へー。たしかに Ruby だと 30 秒くらいかかる。よくある * や + を入れ子にしてバックトラックが~というのではなく、単純にマッチ開始位置とマッチ失敗までのスキャンとがともに入力に比例する N^2 時間かかってる雰囲気。マッチテスト開始位置をずらしたときに「おや、ここはもう通った道だ」と気が付ければいいんだろうか。文字列内の位置とエンジンの内部状態が一致するならすでに出ている結論を流用できる、的な。テキトー言ってるけども。シチュエーションを限定しないとどんだけコストがかかるか想像できないけども。■「正規表現の脆弱性 (ReDoS) を JavaScript で学ぶ


2022年02月17日 (木) [AtCoder] 精進。先週末の ARC135-C「XOR to All」(水 diff)。XOR なのでまず考えたのはビットの分離。最大で 30 ある各ビットが A 数列を通していくつ立っているかを数えた。その数が N の半分より多いならそのままで、半分より少ないならビットを反転させたとして最大値を計算してみた。サンプルの1が合わない。どうやらビットごとに好きなビットを立てたり下げたりすることはできなくて、ビット間に拘束条件があるらしい。しばらく具体的に考えてみた。Ai を作用させてみて、Ai を作用させた Aj を作用させてみて……、そうすると Aj を作用させた段階で最初に作用させた Ai の効果が消えるなあ、と。提出 #29378726 (TLE×31)。これがコンテスト終了1分前の提出でなければ効率を考えて清書することができた。TLE の背後に WA が隠れていたりはしなかった。■提出 #29378726 (AC / 253 Byte / 1197 ms)。提出 #29378845 (AC / 202 Byte / 990 ms)。■以前日記にこう書いた。「コンテストの時に限って頭ヨワヨワの神経衰弱になること、あると思います」。この問題に当てはめるとそれはこう。「制約に Ai が 2^{30} 未満ってあるけど、Ai が取り得る値は 30 ビット符号無し整数の範囲だろうか、それとも 29 ビットだろうか。2^{30} は 1<<30 だろうか 1<<29 だろうか。(1<<30).bit_length は 31 かな、30 かな。Ruby で Integer#[] メソッドを使って整数の右端のビットを得るときの引数は(たぶん) 0 だから、引数の範囲は 0..29 だろうかそれとも 0..30 まで必要だろうか。ぐるぐるぐるぐる」 添字と序数とサイズと左シフトの回数がそれぞれ微妙に異なっていたり一致していたりするのに答えが出せなくて、無為に時間が過ぎていく現象のこと。


2022年02月14日 (月) [AtCoder] 精進。第九回 アルゴリズム実技検定 過去問I 問題 直通エレベータ。考えてもわからなくて飛ばしていた問題。解き直すきっかけはこの前の ABC に関するこれ「時間はかかったけど昨日は直線の上にグラフが見えた」なんだけど、実はこちらの問題は ABC の方より見た目がグラフっぽくてダイクストラ法を使うことは見えていた。見えていなかったのは、エレベーターを使う場合と使わない場合の隣接ノードのみをキューに追加すればいい、ということ。遷移先の候補が多すぎて困っていたのだけど、実はエレベーターを使った移動先と、上下にある一番近くのエレベーター乗降階のみを考えるだけで良かった。提出 #29245963 (AC / 1134 Byte / 703 ms)。


2022年02月12日 (土) GetWindowLongPtr 関数の第二引数(nIndex)について不思議に思った。これはウィンドウごとに割り当てられた値を取得する 0 を起点としたオフセット値だという。正の値はウィンドウクラスで指定して確保させた余分なバイトサイズ分だけ有効。マイナスはポインタサイズだけ有効。その他に Windows が内部で利用するために確保している領域もあり、それらのオフセットは GWLP_XXX として定義されている。GWLP_WNDPROC(-4)/GWLP_HINSTANCE(-6)/GWLP_HWNDPARENT(-8)/GWLP_ID(-12)/GWL_STYLE(-16)/GWL_EXSTYLE(-20)/GWLP_USERDATA(-21)。■第一の疑問。オフセット値はバイト単位なの? たとえば 6 バイト余分に確保させたとして、有効な正の範囲は 0-5 になると書いてあるけど、5 を指定して返ってくるポインタサイズの整数(LONG_PTR。32ビット/64ビット)の中身はどういうものになるのか。有効な値は 1 バイト分だけで残りはこちらのあずかり知らぬゴミになるのかどうか。■第二の疑問。これも同じ。オフセット値がバイト単位だとして、予め定義された定数が必ずしも 4 バイト単位でなく半端な値なのはどういうことか。GWLP_WNDPROC(-4) と GWLP_HINSTANCE(-6) と GWLP_HWNDPARENT(-8) で返ってくる値は 2 バイトずつオーバーラップしているのではないか。GWLP_HINSTANCE(-6) の肩身の狭さよ。GWL_EXSTYLE(-20) と GWLP_USERDATA(-21) も 1 しか違わない。GWLP_USERDATA(-21) から始まる 32ビット/64ビット がプログラマに開放されているなら、GWL_EXSTYLE(-20) のための領域はどこにあるのか。■第三の疑問も同じ。単位がバイト単位だとして、GWL_XXX という GWLP_XXX の下位非互換定数が #ifdef _WIN64 という分岐によって #undef されているのに対して、GWLP_XXX 定数が _WIN64 によらず概ね 4 バイト単位の単一定義なのはなぜか。GWLP_WNDPROC(-4) を -8 と定義しないでもいいのはどういう理屈か。■第四の疑問。マイナスの値はポインタサイズ分だけ有効と書いてあるけど、そのあたりの値は GWLP_WNDPROC(-4)/GWLP_HINSTANCE(-6)/GWLP_HWNDPARENT(-8) によって予約されているように見える。だとすれば、「有効なオフセットは 0 以上でウィンドウクラスで指定した cbWndExtra のサイズ分だけ有効。それ以外の値を取得するにはオフセットとして以下の定数を……」と書けばいい話で、あえて「負の値はポインタサイズだけ有効。それ以外の値を取得するには……」と負の値を括り出す理由はないように思える。■1つ2つの勘違いがないと説明がつかないように思う。どこを間違えている? GetWindowLongPtr のソースコードを読ませてくれ。■SetWindowLongPtr(hwnd, 0, 0x0123456789ABCDEF) と SetWindowLongPtr(hwnd, GWLP_USERDATA, 0x0123456789ABCDEF) した結果がどう読み取られるかを cbWndExtra とオフセットを変えて調べてみた。cbWndExtra7.png, cbWndExtra8.png, cbWndExtra9.png cbWndExtra16.png。オフセット値は 1 バイト単位であり、取得できる値はオーバーラップしている。ただし LONG_PTR サイズの読み取り枠が cbWndExtra のサイズ内に収まっていなければいけない。64ビット環境の場合はつまり、cbWndExtra が 8 未満の時に値の設定と取得はできない(0 が返ってくる)。8 バイトを確保しているときはオフセットとして 0 のみが有効。GWLP_WNDPROC(-4) と GWLP_HINSTANCE(-6) は 2 バイト(0x14a0) を除いて値が共通しているが共通部分の値がシフトしているわけではない。GWLP_USERDATA(-21) の値(8 バイト)がどこに保存されているのか謎。cbWndExtra の使い方はわかったけど既定のオフセットの意味はやっぱりわからない。負値はオフセット扱いじゃないんかな。じゃあ負のオフセットは LONG_PTR サイズまで有効とか書くなって話なんだけど。■わかった!!!Valid values are in the range zero through the number of bytes of extra window memory, minus the size of a LONG_PTR」を読み間違えていた。有効な値は 0 から cbWndExtra までと 0 からマイナスポインタサイズまでではなく、0 から「cbWndExtra マイナス ポインタサイズ」までだった。えええええ、そこにカンマを入れる? そしてカンマひとつでここまで勘違いをひきずるか>自分。■オンラインでは見つけられなかったけど PC 内に日本語訳を見つけた。「nIndex 取得する値に 0 から始まるオフセットを指定します。有効なオフセットは、0 から拡張ウィンドウメモリのバイト数 -8 までです。たとえば、拡張メモリが 24 バイト以上ある場合、16 を指定すると、3 番目の整数値が取得できます。その他の値を取得するときは、次のいずれかの値を指定します。 」 具体例まで追加していい仕事してますよ。見つからんかったけど。


2022年02月11日 (金) Excel の罠。表データをフィルタしてから重複データの削除を実行したんです。俺は重複の削除もフィルタの一種だと思ってるし、フィルタを適用する順番には意味があるつもりでいるんだけど、Excel くんはフィルタされて見えていない行を含む全件データを対象にして重複を削除してしまったんですな。そうするとフィルタを通して見えていたデータが重複行であるとして消されてしまうことがある。2行や3行が1行にまとまるのでなく、ゼロになってしまうことがある。Excel を使う人の 10 人に 5 人がこの罠にはまって煮え湯を飲まされた経験があると、俺は思うね。これが SQL なら WHERE 句と HAVING 句を間違えるような真似はしないんだけど、Excel には知識も経験も不足している。■条件範囲という Excel ならではの絞り込み条件の指定方法を学習した!■ユーザー設定のビューという機能の存在を知った!■■■衝撃の事実。「今回のエントリーの中で一番の目玉がテーブル機能です。歴史は意外と古くExcel 2000(コメントより修正しました)より登場したものです。テーブルという名前からわかるように、Accessのようなデータベースのテーブル形式にしてExcelのシートを使う機能であるため、使いこなせれば非常に楽に作業を進めることができます。しかし、Excelをただの画面レイアウトの方眼紙程度にしか捉えてない人だと、その仕組に挫折することがあるようです(Accessで挫折する人が出るのも同じ理由だったりします)。」 テーブル機能が表計算ソフトの基本機能ではなかったことがある。どこを探せば欲しい機能が見つかるか目星がついてきた。


2022年02月06日 (日) [AtCoder] 昨日あったモノグサプログラミングコンテスト2022(AtCoder Beginner Contest 238)。いやあ前代未聞だった(別に不祥事とかではない)。A 問題より簡単な D 問題。B 問題 D 問題より難しい A 問題。統計的評価は知らないけど、自分の尺度ではそう。A 問題に 8 分かけたのに対して D 問題は具体例を使って解答を検証していた時間を含めても 6 分なんだから。どう考えても過度に問題を単純化する誤りを犯しているとしか思えなかったので、提出前に時間をとって粗探しをしていた。粗はなかった。C 問題に 30 分、E 問題に 50 分かけたので、収支は均衡してしまったけど。■終了 5 分前に E 問題を AC できたのはこのときの経験が生きていたから>「BFS とは思いもよらなかった。たぶんグリッドでなくほぼ直線だったからだろう。そういう先入観でプランBが見えなかった」。時間はかかったけど昨日は直線の上にグラフが見えた。■難しかった A 問題。いくら多倍長整数がシームレスに利用できる Ruby といえども、2 の N 乗(ただし N の上限はおよそ 2 の 30 乗)という値を実際に作成するのは怖くてできなかった(試してみると警告付きで Infinity が返ってくるようで、AC をとるのに支障はなかったみたい)。N がある程度大きいときを予めふるいにかけておいて、N が小さいときと保険のために N が境界付近のときは問われている式を素直に評価した。提出 #29071747 (AC / 64 Byte / 61 ms)。■Ruby で提出している人が多く見えたので G 問題「Cubic?」を考えてみた。普段 G 問題は読みもしない(F 問題が解けていないので)。提出 #29125349 (TLE×26)。複数のクエリに答える問題なので TLE は惜しくない。TLE を出さないところが問題の問題。でも AC が 44 個あって WA がひとつもないのは嬉しい。


2022年02月02日 (水)

最終更新: 2022-02-11T22:02+0900

[Ruby] Ruby が謎に止まる。(解決済み)

先日解いていた「JOIG 2021/2022 本選競技課題」(テストケースのダウンロードができる) の F 問題へのこの提出に関して>20220129p01.05

Hash#shift を意味がある限り繰り返したあとで Hash#clear を呼ぶようにしている(25 行目)。shift は破壊的なメソッドだから shift を十分に呼び出した後の Hash は空になっているはずで、clear を呼ぶ意味はなさそうに思える。それでも呼ぶようにしているのは、これがないとテストケースの in/05-01.txt に限ってスクリプトの応答がなくなって TLE になると思ったから。少なくともローカル Windows 環境では Ruby-2.7 と Ruby-2.5 で止まる。Ruby-1.9 は平気。他のテストケースでは止まらないけど、問題のケースでは必ず止まる。止まるタイミングはバラバラだけど、いずれも Hash への代入で止まっているようだった。

再現スクリプトはこんな感じ(これを見つけたから日記に書いている)。他所で再現するかは、どうでしょうね。

Many = 100 # 10 回では少ない。テキトーに大きく。
Some = 100 # ウチでは 30 回目までは到達しない。
# ウチの Ruby-2.5: ruby 2.5.5p157 (2019-03-15 revision 67260) [x64-mingw32]
# ウチの Ruby-2.7: ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]

Some.times{|some|
	warn "#{some} begin"
	hash = {}
	Many.times{
		some.times{|j|
			k = Random.rand 0..Many
			hash[k] = 1 # maybe hang
		}
		0 while hash.shift
	}
	warn "#{some} end"
}
warn :exit

Ruby-2.5 と Ruby-2.7 では "N begin" (N は 20 前後) を表示して止まる。2.5 より 2.7 の方が早く止まるという傾向はあるけど、具体的な回数にはバラツキがある。それはまあ乱数を使ってるからだと思うけど、ハッシュのキーを k に代えて j にしたり、シャッフルした順列にしたりすると止まらなくなるので(正常動作)、乱数は外せない。

こういう Issue「Bug #17779: 特定の順序でHashのkeyを削除した場合に Hash#first が遅くなる - Ruby master - Ruby Issue Tracking System」を思い出したけど、全然関係はなさそう? 全然わからない。

AtCoder のコードテストを使わせてもらったら(目的外失礼)、こういう結果。AtCoder の Ruby も今のバージョンは 2.7。

終了コード9
実行時間10501 ms
メモリ9112 KB

標準エラー出力は 30 begin で途切れている。完走しても 10 秒もかかるはずないから、ハングして実行を打ち切られたのだと思う。コードテストとジャッジサーバーが同じだとして、ジャッジサーバーのスペックはこう>Linux ip-***-***-***-*** 4.15.0-1041-aws #43-Ubuntu SMP Thu Jun 6 13:39:11 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux*

 ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x64-mingw-ucrt]

Ruby-3.1 が「ウチの Ruby」に今日加わりました。大体ループの 20 から 30 回目で止まるんだけど、弾みで 2 回だけ完走したりもした。

 テスト(改訂版)

パラメータが2つもあると面倒なので。

改訂するにあたって予想外に止まらなくなったりした(正常動作)のを通して、たぶん直接的な原因もわかった。

# ウチではだいたい 20 から 30 回で "empty?: true" を最後にして止まる。
# ウチの Ruby-2.5: ruby 2.5.5p157 (2019-03-15 revision 67260) [x64-mingw32]
# ウチの Ruby-2.7: ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]
# ウチの Ruby-3.1: ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x64-mingw-ucrt]

H = {}
100.times{|n|
	while H.size < n
		k = Random.rand 0..1<<30
		H[k] = 1 # たぶんここで止まる。
	end
	warn "size: #{H.size} before shifting."
	H.shift until H.empty?
	raise if H.shift # no exception. だけど Hash が空になった後の shift が後でハングをひき起こす?
	warn "empty?: #{H.empty?}"
}
warn :exit

Hash を空にする方法として 0 while H.shift を選ぶとその後の Hash#[]= でハングするが、Hash を空にする方法として H.shift until H.empty? を選ぶとハングしなかった。条件を揃えていくと、Hash#empty? が作用してハングを回避しているのではなく、Hash が空になったあとの Hash#shift がハングをひき起こしているようだった。ちなみに Hash#clear にハングを回避する効果があるらしいのは、JOIG の問題への提出で確認済み(とはもう書いた)。

 対応していただきました! (Bug #18578)

投稿して次に見たときにはすべてが終わっていた。早いぜ。

こういう仕組みだったようです。

メモ:entries_bound は使用中のビン(DELETEDになったビンを含む)の数に(ほぼ)対応していて、これをみてテーブルをリビルドしている(rebuild_table_if_necessary)。空のハッシュに対する Hash#shift はなぜか entries_bound を 0 にしているので、リビルドすべきタイミングを逃し、ビンがすべて使用中になった状態で空きビンを探そうとするので無限ループに陥っていた(find_table_bin_ind)。

* [[Language Test 202001 - AtCoder|https://atcoder.jp/contests/language-test-202001]]

 [[AtCoder 2019/7 Language Update - Google スプレッドシート|https://docs.google.com/spreadsheets/d/1PmsqufkF3wjKN6g1L0STS80yP4a6u-VdGiEv5uOHe0M/edit]]


2022年02月01日 (火) 最近になって Dropbox から壊れた HTTP レスポンスヘッダ Strict-Transport-Security: max-age=31536000; includeSubDomains, max-age=31536000; includeSubDomains; preload が送られて来るようになって困っている。二重指定とカンマ区切りと。Firefox はコンソールに Strict-Transport-Security: サイトに指定されたヘッダーを正しく解析できませんでした。 とメッセージを出してページを処理してくれないのだけど、他に困っている人はいないのか。


2022年01月31日 (月) [AtCoder] 昨日あった ABC237提出一覧。B 問題がなんかやけに難しいなと思いながら、だけど ABC-B だからってなめてないので謙虚に(それほど)焦らずに解いて提出したら、サンプルから何からすべて WA で、デバッグ出力を消し忘れたかと見直したけどそんなことはなくて、問題を読み直してみたらさっき解いた問題と全然違う! C 問題もさっき解いたのとは違う。D 問題を開いてやっと見覚えのある問題が見つかった。そんな間違いある? 2行もずれてクリックミスしたの? いつも問題ページから提出するのに問題と提出先が食い違うってことある? と不思議に思いながら A>D>B>C>E といつもと違う変則的な順番で解いていた。■質問タブってコンテスト中も終了後も全然見ないんだけど(自分とはほぼ無関係だから)、そういえばコンテスト中にピコンとページ上部に動きがあったなと見てみたら、どうもこういうことが起こっていたらしい。「B問題に誤った問題文が入っておりました。現在修正が完了しております。なお、元のB問題はD問題であるため、そちらを解いた方はD問題に提出してください。」「D問題の回答をB問題に提出してしまった方は、コンテスト終了後5分以内に質問タブからご連絡ください。順次提出を無効化いたします。」 知らないよー。■舞台裏。「社長が1個1個手作業で無効化しているので、Rating更新少し遅れます……ごめんなさい!!!」「コンテスト中1時間半ずっとこれ対応してて、今1/3のところなので、あと3時間くらいで終わると思います……!」■「【ABC237】D問題→B問題への誤提出ですが、先頭の300件の質問から、 「'L'」「'R'」「"L"」「"R"」「76」「82」 の6種の文字列が含まれているかどうかで、誤提出を100%検出可能であることを確認しましたので、機械的に誤提出への対応を行います。当てはまらない方がいたらご連絡下さい。」 Ruby なので 'L' でも 76 でもなくて ?L.ord なのよね。ま、5分(考えようによっては5分50秒)の違いは大差ないやろ。■@2022-01-31 01:12 提出一覧から 2022-01-30 21:25:29 の提出が消えたみたい。ペナルティ5分が消えて嬉しい。


2022年01月30日 (日) Sony Reader が現役なのにも関わらず Reader Store を使わなくなった理由は、支払い方法がクレジットカードに一本化されたからです。PlayStation のディスクモデルを必要としている理由は、CERO Z 指定のゲームを PS Store で買うのにクレジットカードの登録が求められるからです。俺はクレカがインフラだとも唯一の(そして真っ当な?)成人認証手段だとも思わんよ。販売者が代替手段で楽をするから俺は客になれない。


2022年01月29日 (土)

最終更新: 2023-07-22T23:29+0900

[AtCoder] JOIG 2021/2022 本選 過去問/F 問題 タクシー 2 (Taxis 2)

 ステップ1

  • タクシーが赤色の場合,乗車後の所持金が a−1 円になる.
  • タクシーが青色の場合,乗車後の所持金が「a÷2 を整数に切り捨てた値」円になる.

町 1 から出発し,1 円以上の所持金を残した状態で町 Tj​ に到着するために,最初に少なくとも何円の所持金を持っている必要があるか

問題のこの部分は、まあ、逆に考えます。所持金が1の状態でスタートして、所持金が +1 になる、×2 になる、というように。そうすれば初期金額を探索しなくて良い。ただしそれぞれの町をスタート地点に設定して町1への最短経路を繰り返し求めるということは許されていないので、スタート地点は町1のままにしておきたい。

ところが町1を1円でスタートして、+1/×2 しながら各町に着いたときいくらになっているかをダイクストラ法で求めてもサンプルが合わない。合わなかった。

たぶん、-1/÷2 の逆計算は +1/×2 でいいのだけど、((((-1)-1)÷2)-1)÷2 の逆計算は ((((×2)+1)×2)+1)+1 ではないとか、合ってるけどその通りに計算できていないとか、そういう理由。

わからんなりにテキトーに 2×2 の正方行列に 2 とか 1 とか 0 を割り当てて実際に計算してみれば、必要な係数が2つだということと、それをどう変化させればいいのかがわかった。スクリプトの中の x,y = x,x+yx,y = x+x,y というのがタクシーごとの変化。式を見ても意味はわからない。

 ステップ2

逆計算がわかったからダイクストラ法で解けたかというと、小課題の4あたりから答えが稀にあわなかったり時間をオーバーしたりする。した。小課題の3までは問題のグラフが木だから複数の経路の競合がなかったのだな。

これはたぶん、ある町における状態というのが所持金というパラメータ1つで優先順位を付けられないせいだと思う。さっき x と y の式を2つ書いたけど、そして所持金というのが d = x+y で表されるのだけど、ある町からある町へ移動するにあたりどちらのタクシーを使っても所持金の変化は x+x+y == d+x で共通している。だけどその次の移動での増加量が x の値にのみ依存するので、x を増加させることで所持金を変化させたタクシーの方が分が悪い。

 ステップ3

2つのタクシーの運賃を普通の経済感覚で比べてみると、どの場合でも所持金が1円減るだけの赤色のタクシーを利用できるときは利用して損をすることがないことがわかる。たぶん。01BFS みたいなステップを踏めば解けるような気がするなー。

 提出 #28854159 (AC / 1242 Byte / 2410 ms)

頭の中が整理されていないので無駄がまだあると思う(嘘解法の可能性も大いにある)。でも1秒しかなかった JOIG-2021-F「デジタルアート (Digital Art)」(20211210p01) と違って制限時間が4秒もあったので間に合った。嬉しい。

D 問題まではやるだけだったけど、E 問題「エゴイ展 (EGOI Exhibition)」がまだ解けていない。価値の正負と絵の種類で場合分けとクラス分けをして DP をしようとしたんだけど……。


PQ#dn_heap がバグってるね。不等号の向きが逆。これは要するに、PQ には繰り返す幅優先探索の無駄を気休め程度に取り除く意味しかなく、実際にはその効果さえなかったということ。

 提出 #28975251 (AC / 502 Byte / 1158 ms) ← 2410 ms

PQ#dn_heap のバグをとったとしても定数倍が重いせいで、初期パラメータが異なる複数の地点をスタート地点に設定した幅優先探索のようなものを、距離の更新がなくなるまでぐるぐる回した方が速い。優先度付きキューを使うと確かに最短距離を複数回更新するという無駄はないのだけど。

Array をキューにするのと Hash をキューにするのでは Hash の定数倍が重い。キュー内で要素が重複することを気にせず Array につっこんだ方が速かったりする。

そんな(良くない)理由でメインループ内の前半のループのキューは PQ でも(後半のループのように) Hash でもなく Array になっている。

 提出 #28988344 (AC / 600 Byte / 863 ms) ← 1158 ms

キューを2本持つことでプライオリティキューを使わないでもソート済みの状態を保ちながらキューを伸ばせる場合があるというのは、AtCoder について書いた2番目の日記に書いてある>20190916p01.01。今回もこれが使える。

863 ms というのは Ruby に限らない AC の中で1ページ目に入っているのだ(全部で9ページではあるのだけど)。