/ 最近 .rdf 追記 設定 本棚

脳log[2023-07-23~]



2023年07月23日 (日) [AtCoder] 精進。ABC142-F「Pure」(青 diff)。すんごく難しかった、というか、ややこしかった。これってうまい書き方があるんだろうか。「すべての頂点の入次数が 1、出次数が 1 であるような G の誘導部分グラフ」というのは要するに輪っかのことで、しかも輪っかを横切るショートカット辺が存在しないもののこと。N の2乗が通る制約ではあるけども、すべての頂点を始点にして輪っかを検出して、輪っかのパスを辿ってショートカット辺が存在しないことを確かめて許されるのかどうか。■提出 #43905266 (AC / 967 Byte / 77 ms)。再帰関数のパラメータとしてパスを表す配列と、再訪確認のためにパスをメンバとするハッシュ表と、調べたけどダメだったことをメモするハッシュ表を渡している。もっとうまいやりようがありそうなものだ。■輪っかの検出ロジックは、終点を決めて、終点の隣接頂点の1つを始点にして終点への到達可否を調べる。そのときに始点や中継点において、終点に到達したパスの中に2つ以上の隣接頂点(←始点や中継点にとっての)が含まれていないことを確認している。2つ以上の隣接頂点が関わる輪っかはショートカット辺がある輪っかなので。これを愚直に繰り返すと TLE になったので、ダメだった結果をメモしている。輪っかにならなかったケースとショートカット辺があったケース。メモの利用は中途半端で、輪っかの終点を切り替えるごとにリセットしてしまっている。輪っかなんだから(輪っかを検出するための便宜上の)終点をどこに取ったとしてもショートカット辺があるせいでダメだったという結果は共有できるはずなんだけどしていない。あと必要だったのは終点を含まないところで輪っかができてしまうケースの除外。それはいま関心がある部分ではないので後の処理を待ってほしい。再帰関数で同一頂点への再入を検知してリターンしないとスタックを使い尽くしてしまった。■これって強連結成分を定型的に列挙してショートカット辺をちょちょいと調べて終わりだったりしたのでは? 自分は何か輪っかの検出で苦労して、輪っかの検出とショートカット辺の確認を同時にやろうとしてさらに苦しくなっている雰囲気。DFS 2回の SCC 分解がそらで書けるほど身についてないから仕方ないんだけど。■日記を書くために自分の解法を書いていたらこれがなんで AC なのかわからなくなってきた。それでひとつの発見があった。最初に「要するに輪っかのことで、しかも輪っかを横切るショートカット辺が存在しないもののこと」と書いたけど、ショートカット辺はただのショートカット辺ではなくて長さ1のショートカット辺だった。誘導部分グラフに含まれる辺と含まれない辺を考えれば当然のことだけど。そうするとひとつの疑問がわいて、どんなループでも、許されない長さ1のショートカット辺を本式の、ループを構成する辺として採用することで答えにすることができるのかどうか。できそうな気がする。それで実装が簡単になるかと思って別解を書き始めたけど、べつにそんなことはなかった。■提出 #43935918 (AC / 1048 Byte / 69 ms)。長くはなったけどややこしさは減って、シンプルに閉路検出&コンパクト化で答えが得られるようになった。■この問題を選んだきっかけはこの前の ABC311-C「Find it!」で閉路検出を書いたからで(#43848448)、その目論見が外れてやけにややこしい解法に当初はなったけど、別解ができてみると狙いが外れていなかったことがわかる。グラフの形がちょっと複雑になって、閉路のコンパクト化という処理を追加したものがこの Pure という問題だった。


2023年07月21日 (金) [AtCoder] 精進。東京海上日動プログラミングコンテスト2023(AtCoder Beginner Contest 299)-G「Minimum Permutation」(黄 diff)。愚直解法が作れたら、あとはセグメント木を知っていますかというだけの問題になる。愚直解法とは。M 個の数について末尾の位置を求め、その最小値(もっとも前の位置)を I とする。I とはどういう数か。A 数列のうち I までの範囲からはどんな要素を選んで先頭に配置しても良い。なんなら I 番目を先頭にしても良い。そうしても1から M までの数が欠けることがないので。もちろん最小の要素を選んでこれを答えの先頭の要素とする。以降は選んだ値を取り除き A 数列の範囲を限定して繰り返し、2番目3番目……M 番目の要素を選んでいく。■提出 #43807822 (AC / 757 Byte / 1384 ms)。実装はごくごく素直で難しくない。Ruby によるすべての提出を見ると、セグメント木とヒープを併用するもの、セグメント木と二分探索を併用していて倍速いもの、Array#tally と while ループでゴルフをしているくっそ速いものなどいろいろ。自分のはセグメント木を2つ使っていて一番遅い部類。1つ使ったら2つ使うのも同じだと思ったけど、定数倍の面で最善の選択ではなかったみたい。セグメント木を使わない解法はさっぱりわかんない。セグメント木を使わないで N^2 にならない理由がわからない。■末尾の位置の最小値は最初にソートしておけば Array#shift で更新していける。で、その最前線に向かって増加列を作る。最前線を更新する。増加列を作る。繰り返し。みたいな?■提出 #43911137 (AC / 277 Byte / 278 ms)。そうみたい。Array#tally と、答えの配列(B)と、答えの配列のどこまでが確定しているか(i)と、値から答えの配列の添字を引く逆引きインデックス(I)を使っている。答えの配列の後半の未確定部分に増加列を記録している。最初は配列を2つ使って答えと増加列を保存していたけど、1つの配列でいいなと。そうすれば値の移し替えがいらないなと。最初は増加列の中にすでに同じ値が含まれているかどうかを調べるのに二分探索を使っていたけど、逆引きインデックスがあれば定数時間だなと。tally の使い道はある値が最後に出現する位置を知るためなので、ゴルフをしているのでなければ Array にくらべてオーバーヘッドの大きい Hash を使うことに合理性はない。すでに 213 ms の提出が存在している。■伝わらないことを承知で書くけども、「N^2 にならない理由がわからない」と書いていたときには、増加列を途中までコミットするという操作が欠けていた。どういうことか。各数字の末尾の位置の最前線に到達したときに、必要に迫られて増加列を答えの数列の一部としてコミットする。そのときにコミットした最後の要素の次の位置からまた増加列の作成を開始するなら、それは手戻りであり線形時間の処理では済まなくなる。増加列の前半だけを答えにコミットして後半を残しておくことで、増加列の作成を現在の位置からそのまま継続することができる。手戻りがない。最初はこの手戻りが解決できなくて解けなかった。次にセグメント木を思い出して解けた。しかし NlogN の時間がかかる。途中までコミットするという操作を発見してとうとう線形時間の処理になった。こういう工夫のしがいのある問題好き。たとえば「Simplified Reversi」(20200920p01.03) とか。


2023年07月17日 (月) 「あたま よわい」「顔 強い」 簡潔すぎる処方薬の説明にツッコミ殺到 思わずドキッとする表現に「声出して笑った」(1/2 ページ) - ねとらぼ」■これってやさしい日本語ってことなんだろうか。かえって難しいと思った。副詞を使うことも許されないの?


2023年07月16日 (日) [AtCoder] 精進。昨日あった freee プログラミングコンテスト2023(AtCoder Beginner Contest 310)-E「NAND repeatedly」(水 diff)。実は確かめるまでもなくサンプルの1に書いてあったのだけど、「⊼ は結合法則を満たさない」。だから右側からの累積和を使って左側から数え上げていくことはできない。できない? 昨日は左側からの累積和をどうにかしてうまく数えられないかと四苦八苦していたが、成果なし。一日経ってあらためて NAND について解釈してみた。右側から0を作用させるとき、左の値によらず1になる。右側から1を作用させるとき、左の値が反転する。右側から0を作用させたときに無条件に値が決まるから結合法則が成り立たなくなったりするんだろうか。ありがたくない性質。だけど f(i,j) を考えるとき、i と j のあいだに 0 があるなら i の値によらず f の値が決まるということでもある。i から j に向かって値を作用させていくとき、0の位置で f 値が1に固定されるわけなので、以降は共通。■提出 #43667957 (AC / 450 Byte / 528 ms)。コメントに書いたけど、0 の位置をまず調べて、その隣接する位置を i<j とするとき、「f(,i)=1 として f(,k) (i<=k<j) の和」「それの累積和」を考えた。できないと思ったけど結局右側からの累積和で解いている。現在の位置より右にある最初の0以降を範囲の右端とする f の値は累積和を引いてわかる。最初の0までにある1の連続を右端とする f の値は 01010... か 10101... のどちらかなのでこれもすぐにわかる。■昨日は考えずにテキトーに累積和というのか4つの値を記録する DP というのかをがちゃがちゃしていたのがまずかったな。いや違う。考えてはいた。袋小路だっただけで。Ruby によるすべての提出を見ると、自分のは長いし遅いし、かなり回りくどいことをしているみたい。えっっっ?■F 問題「Make 10 Again」は正解方針が引けなかったな。「確率とか期待値の問題って方針を決めるところに難しさがある。正解方針が引ければ答えが合うけど、答えにたどりつけない方針を引いてしまいがち。当てもんをやっている」。D 問題「Peaceful Teams」を踏まえて和が 10 になる組み合わせを全部列挙していいのかと思ったけど、包除がうまく扱えなかった。和が 10 になるまでの DP もやったけど合わない。たぶんだけど「~が存在する」確率を尋ねてはいけないのではないかと思う。解けないから。作問者は反省してほしい。■D 問題までも書いておこうね。自分のすべての提出。AC までに使った時間が A=1分半、B=7分半、C=3分、D=29分。以下ふりかえり。■A 問題「Order Something Else」。候補は2つ。定価もしくは余計なものを買って割引価格。■B 問題「Strictly Superior」。価格とスペックの比較。価格で負けていなくて機能で勝っているか、機能で負けていなくて価格で買っているか。100 ビットのビット演算でやったけど、機能の比較が間違いやすそうですごく緊張した。f1==f1&f2 とか f2==f2&f1 とか、いかにも取り違えそう。幸いペナルティは免れた。■C 問題「Reversible」。順逆を区別しない文字列の同一性判定。辞書順で先か後かどちらかを順逆の代表にすると決めて Hash に突っ込むだけ。■D 問題「Peaceful Teams」。これが時間内に解けた最後の問題。制約が 10 以下と小さいけど、さすがに T(=10) の N(=10) 乗は許されない。どうやってチームを作ろうか。最初は仕切りを使って T チームの人数配分を決めた後で N 人の順列を当てはめようとした。たとえば N=4人、T=3チームのとき、チームへの人数配分が 2-1-1 の場合を考えると、そこに配置する人の並びが 12-3-4、12-4-3、13-2-4、13-4-2 などになる。だけど重複がありますね。2人チームに人1と人2を割り当てる場合と人2と人1を割り当てる場合を区別してはいけない。まだある。2つある1人チームに人3と人4を割り当てる場合と人4と人3を割り当てる場合を区別してはいけない。同一人数のチームの並びと、同一チーム内での人の並びを区別しないようにサイズの階乗で割り算する必要がある。そうやって個々のケースに対応するなら場合の数を考える意味がない。相性問題への対処もわからない。もう手作業で個別具体的にチームを組んでいいんじゃないか。最終的に採用した方針がこう。T 個の空のチームを予め用意する。人1から順番に参加するチームを総当たりで試させる。そのときに相性を考慮する。N 人全員が行き先を決めたときに T チームすべてに人がいたならカウント1アップ。1つ1つ数えて間に合う制約なんです。相性チェックも総当たりで突き合わせて全然問題ない。処理の流れさえ決まればやることは愚直でいい。再帰関数を使った深さ優先探索で。これが T(=10) の N(=10) 乗でない理由は、部屋というのを構成メンバーで区別しているから。16 行目の「break if is.empty?」がポイント。空きチームが5個あるときに、5個ともに入ってみる必要はないよね。■■■E 問題。kotatsugame さんの動画を見ていました。「j を動かしながら今0のやつと1のやつの個数を求めておけばいいですね。」 あっはい(その通りですね)。(動画中断)。提出 #43692228 (AC / 144 Byte / 138 ms)。ちょー簡単だった! 脳みそがお留守だったと言うほかない。


2023年07月13日 (木) [WR250R] ヘルメットが新しくなった。新しいのが SHOEI VFX-WR ALLEGIANT TC-3 BLUE/WHITE L-size。古いのが SHOEI HORNET 銀色 L-size。現行の HORNET (オフロードタイプかつシールドタイプのヘルメットの名前)が HORNET-ADV で、廃盤商品を見ても HORNET-DS しかないんだけど、HORNET-DS の性能比較画像に比較対象として無印の HORNET が見える。それ。新しいのはシールドタイプではない。HORNET を使っていてわかったのだけど、晴れの日はシールドを上げて風を受けたいし、雨の日は雨粒がバチバチ当たって痛いので当然下げたいけど、シールドが汚れていたり眼鏡とシールドがどちらも雨粒で濡れていると見えにくさが2倍でとても下げていられないので、シールドは出番がない。だから最初からなしでいい。■10 年も経つとね、ウレタンフォームが痩せてついには黒い粉に分解するんですよ。全部が同じ物質か知らないけど、ウレタンとかポリウレタンとか名前につくものは空気中の湿気を吸って経年で劣化することが宿命づけられている。大事にしまい込んでいても同じ。ポリウレタンで伸縮性を確保している服とか、PUコーティングの簡易防水バッグとかは、大事に使うようなものではない。ヘルメットも一般的にはそうみたい(性能を維持するために買い換えるのがいいと言われる)。今のバイクを買った8年前にしまいこんでいたヘルメットを引っ張り出してきたらスポンジが粉になっていたので内装だけ買い換えたんだけど、すでに公式の販売は終了していてすぐには手に入らなかったんだけど、こまめに洗っていたからかその新しい内装がまた粉になったし、雑に外そうとしたチンストラップカバーが紙のように裂けたしで、もういいかなと。全然使わなかったシールドは外したままなくなっていたし、チークパッドは新しいのが手に入らなくてぺちゃんこのを付けてても意味がないので外していたし、プラスチック製のバイザースクリューも買い足したものが少し前にまた砕けていたし、もういいかなと。すでに十分だったよなと。たしか古いのが3万円くらいで、今はそれが倍くらい。セールで1万安くてグラフィックで1万高くて、やっぱり倍くらいした。たっか。10 年後も使っているつもりなら代えの内装とバイザースクリューを確保しておくべきだけど、バイザースクリューは5個入っていてそのうち2個が予備だった。たぶん同じだろうと今のメットから無事な1個も確保した。内装を手に入れるタイミングが難しい。一緒に劣化されては困るし、販売が終了しても困るので。ちなみに内装一式は1万円ちょっとします。これはそんなに変わってないのかな。知ってる最初から高い。


2023年07月11日 (火) [AtCoder][Ruby] 言語アップデートにより次の Ruby は 3.2.2 になる見込み。Ruby は定数倍の違いで AC になったり TLE になったりすることがままあるので、ぎりぎりの問題がどうなるのか興味がある。20230619 で解いた Good Vertices は書き方の違いで TLE と AC が分かれた問題なのでちょうどいいだろう。1995 ms でぎりぎり AC だった提出 #42753403 をコードテストで実行してみた。1998ms@271.png1058ms@322.png。わお、倍早い。他にも Ruby-2.7 で遅くて Ruby-3.1 では(ということは当然 3.2 でも)改善しているものに素数判定があって、ruby27 -rprime -e "p (2**61-1).prime?" は全然返ってこないが 3.1 では数瞬で true が返る。


2023年07月09日 (日) [AtCoder] 今日は ARC164 があった。くやちい。C 問題が AC だったのがくやしい。コンテスト成績表自分のすべての提出。以下 ABC のふりかえり。■A 問題「Ternary Decomposition」。とりあえず3進数を考えます。純粋な3進数と違うところは、各桁の上限が2に限られないということ。どういうことか。n=9 のとき、3^0 ×9個が k の上限で、3^2 ×1個が k の下限。もうちょっと書くと下限は3進数で表したときの各桁の和。その範囲外なら No。範囲内であれば、ある桁を崩して1つ下の桁を3個増やすことができる。つまり2個ずつなら調整が可能。奇数個の調整は不可能。■B 問題「Switching Travel」。白黒白黒……とパスを辿っていったとき、白白もしくは黒黒の辺によって閉路が生じるなら答えは Yes だと思った。それを BFS で実装したら WA だったので UnionFind で実装し直して AC になった。何が悪かったのかはわからない。嘘ですわかります。白黒の辺だけを考えるときグラフが連結だとは限らないので一応すべての頂点を始点にして BFS をしたのだけど、訪問済みフラグを流用していた。だから白白もしくは黒黒の辺で訪問済みの頂点にぶつかったとき、それが異なる連結成分を結んでいるのか閉路なのか区別できなかった。N 回 N 要素の配列を確保することが許されないので流用したのだけど、訪問済みフラグの値を工夫しないといけなかったらしい、BFS/DFS でやるならね。ほら WAAC■C 問題「Reversible Card Game」。B 問題の AC から1時間弱の時間が残っていたのだけど、ほぼほぼ途方に暮れていた。何が最適な戦略なのかさっぱりだった。終了 10 分前くらいから書き始めたのはこういうの。まずカードを裏が大きい(A≦B)と表が大きい(A>B)で分ける。裏が大きい(A≦B)カードについては何もすることがない。だってね、全部のカードが A≦B だったとしよう。Bob は Alice が引っ繰り返したそばからカードを得点に変えていくのが最善。だから他にカードがあるときに Alice はひっくり返さないし、Bob もまだ取らない。では A>B のカードについてはどうか。これは A-B が大きい順に Alice が A を隠す(裏が大きいカードに混ぜる)、Bob がカードを得点にするということを繰り返す。表が大きいカードが1枚だけ余ったらうまいこと考える。それがサンプルの1。なんで終了6分半後に AC だったんだよー。■C 問題はもっと単純だったらしい。「B の大きいペア数が奇数のときは、1 枚だけ低い値をとらされるようだ。低い値は何を取らされる? Alice がどうがんばっても、Bob は最もリスクの低いカードを選ぶことができるようだ。abs(B-A) が最も小さいカードを選べばいい。」 言われればそう。Alice が裏返して隠した表が大きいカードも、後半戦で裏返すそばから Bob に得点に変えられてしまうのだから。


2023年07月08日 (土) [AtCoder] 今日はデンソークリエイトプログラミングコンテスト2023(AtCoder Beginner Contest 309)があった。AC までの所要時間が A=3分(+1ペナ)、B=14分(+1ペナ)、C=5分、D=9分、E=8分、F=49分(+6ペナ)。CDE にくらべて AB の難しさが際立っているね(大嘘)。以下 A-F の振り返り。■A 問題「Nine」。横着をしようとすると案外間違えるグリッドの隣接判定。グリッドで BFS/DFS をするときの有効な移動先に相当する……とか考えたのがペナルティの原因か。「左右に隣接しているか判定してください」という問題だったのに「左右に」を読み落として上下に隣接している場合も Yes と答えてしまった。■B 問題「Rotate」。ABC305-C「Snuke the Cookie Picker」もそうだけど、図形的な操作を人間はひと目で認識できるけど、それをコードに落とし込むのは案外難しいという問題。がんばってやる。ペナルティの原因は1行目と最終行では移動の向きが左右逆だということがコードに反映されていなかった。自分の解答は Array#rotate とか Array#transpose を使うものになってるけど、i とか j とか i-1 とか i+1 とかが入り乱れると絶対に3か所は間違えるので、できるだけ添字を操作しない書き方をするようにしている。DP なんかでも添字を操作しないでいいように Enumerator#with_index とか Array#zip を使う。提出前に修正できたけど今日だって四隅付近で参照する要素を何度も間違えたし。■C 問題「Medicine」。答えは a+1 日目のどれかもしくは1日目。以上。■D 問題「Add One Edge」。入力されるグラフはちょうど2つの連結成分に分かれている。以上。■E 問題「Family and Insurance」。入力は木。この形式の入力を初めて見たのはこのとき>「制約の 1≤pv<v の解釈に一瞬詰まったけど、pv の上限が v であることで、逆向きにスキャンするだけで子から親へ順序よく処理できる親切設計だとわかった」。最近では ABC295-G「Minimum Reachable City」。有効な保険を親から子に伝播させていくか、子が親から引き継ぐかでちょっと迷ったけど、入力の形式から子が親を参照する方が素直に実装できる。その際に番号の昇順に処理することで親の処理が先で子の処理が後になることを保証できる。1代先まで有効な保険は2代に渡って有効な保険だという数字のずれに注意。もうひとつ。サンプルの1が親切だったのだけど、同じ人が複数の保険に加入していることがある。期間の短い保険で上書きしないように注意。■F 問題「Box in Box」。6ペナとバグり散らかした。方針はわりとすぐに決まって、まず入力される3つ組はソートする。縦横高さに3つ組以上の意味はないし、対角線を縦横高さにする問題でもない。3数を A≦B≦C として A の昇順に処理を進めるならあとは B1<B2、C1<C2 となる (B1,C1)、(B2,C2) を見つける問題になる。B を添字とするセグメント木に C の最小値を記録していけば見つけられる。複数の実装ミスを順番に潰していくことで WA×20→WA×11→WA×6→WA×5→WA×2 という経過をたどった。まずそんなにバグらせるなと、そしてバグ修正は律儀に1つずつやらなくてもいいんだと、言いたい。提出のたびに気持ちは G 問題へ移っていたのだけど、WA が出るたびに泣く泣くバグ修正のため F 問題に引き戻された。最初の提出にかけたのは 19 分でそのときに全体の形はできあがっていたのに、その後デバッグに 30 分かけている。G 問題を考えるどころではなく5完と6完の瀬戸際だった。■今日は冴えない日だったな。コンテスト成績表自分のすべての提出(※要ログイン)。


2023年07月05日 (水) 頼まれて家の Wi-Fi の事前共有鍵を入力したとき、「Amazon に保存する」という不穏なオプションのチェックマークはぬかりなく外したのだけど、その後にアマゾンから送られてきた別のデバイスが最初からネットワークへの接続方法を知っていた。不正アクセスでは?■「Wi-Fi設定のAmazonへの保存に関するよくある質問 - Amazonカスタマーサービス」 便利ではなく選ばれにくく選んでもらいたいとも思われていないであろう選択が機能していない疑い。■「Amazonで買うとWi-Fi設定が不要に。スマートホーム規格「Matter」への取り組み - AV Watch」 もちろんロッカーに鍵を預けてはいない。


2023年07月04日 (火) [AtCoder] 精進。ABC009-D「漸化式」(黄 diff)。ABC を古い方から埋めていく取り組みでつまずいてつまずいたままになっている最初の問題。ベルトコンベア式に入力を出力へ変換していく装置がある。10^9 回の処理を繰り返すわけにはいかないのでどうやってプロセスを加速するか。どこかのツイートで行列累乗だと読んだんだよね。たしかに、それ以外にない見た目をしている(しかし気がつかない)。その人は FPS で解くシリーズをやっていたらしいけど。■提出 #43246893 (AC / 351 Byte / 1730 ms)。2回連続で行列の掛け算が書けなかったのがつい2週間前のこと(20230619)なので、今日は3度目の正直。■Ruby での提出一覧を見ると3桁 ms の提出がいくつもある。K^3logM を K^2logM にする Kitamasa 法というものがあるらしい。高速 Kitamasa 法というのは聞いたことがある気がするなあ。


2023年07月01日 (土) [AtCoder] 今日は CodeQUEEN 2023 予選 (AtCoder Beginner Contest 308)があった。コンテスト成績証。今日は F 問題までこれというところのない、つまりは典型的な問題ばかりだった。G 問題は TLE 解しか書けなかった。コンテストの途中から終了後しばらくの現在まで AtCoder にアクセスすると必ず 403 Forbidden が返ってきて、即座にリロードすると正常に表示されるという状態が続いている。とても面倒。以下 A-F のふりかえり。■A 問題「New Scheme」。PAST のはじめの方にでも出てきそうな問題。書かれていることを愚直に実装する。■B 問題「Default Price」。これも愚直に実装する。まだ効率を考えなければいけないような制約ではない。これに5分の時間をかけてしまった理由は入力の受け取りに失敗していたことに気がつくまでの時間。だって AtCoder で色っていったら数字で与えられるのが常なんだもん。■C 問題「Standings」。たぶん分数を浮動小数点数で扱っても AC になるのかなあ、C 問題だし(追記:ツイッターを眺めてるとダメっぽい)。一応 sort メソッドに比較関数を渡して通分してから分子を比較するようにした。浮動小数点数って特殊で使用場所を選ぶものっていう認識で、整数で間に合うところで使うものではないと思ってる。なお JavaScript……。■D 問題「Snuke Maze」。素直にグリッドを探索する。同じマスを二度処理しないようにするなら BFS でも DFS でもいいだろう。再帰関数を使わないで書くなら違いはキューから shift で出すか pop で出すかの違いしかない。■E 問題「MEX」。いいかげん MEX という概念を認知するくらいには MEX を問題で見る機会が増えてきたけど、やっぱり「いずれとも一致しない最小の非負整数」みたいに否定で定義されるのは脳に負荷がかかる。この解法を DP と呼ぶのかな。M の字が出現した状態はその値が 0,1,2 である場合の3通りがある。ME の2字が出現した状態は(0,1,2)×(0,1,2)の9通りが考えられるけど、MEX への影響という観点では M(0)E(1) と M(1)E(0) を区別する意味がない。3桁のビットフラグで8通り持てば十分(ただし、0b000 と 0b111 は0通りに決まってるので意味があるのは6要素)。ところでこの問題の典型要素に「3つのうち真ん中に注目する」があったらしいが気がつかなかった。そういえば ARC160-B「Triple Pair」もそうだったけど真ん中を固定できなかったな>20230514。次からは典型として注目できるといい。■F 問題「Voucher」。ソートすれば貪欲に無駄なくクーポンが利用できそう。何をソートしようか。必ずこうでなければいけないという必然の選択はなんだろう。高額商品は使えるクーポンの幅が広いので低額商品から優先的にクーポンを割り当てたい。あるいは最低価格の設定が高いクーポンは利用機会が限られるのでできるだけ優先的に使用したい。でもクーポンが余るような時に最低価格設定が高いからといって値引きの渋いクーポンを使いたくはない。どうしましょ。低額商品から順番にクーポンを割り当てる。プライオリティキューで使えるクーポンを管理しておいて値引き額が最高のクーポンから順に使用していく。一度使用可能になったクーポンはその後ずっと使用可能だからできる。kotatsugame さんの動画を見ていて言及されていたので思い出したけど、クーポンの値引き額が商品価格を超えないことを制約で確認している。もし値引き額が商品価格によってキャップされてしまうなら常に最大の値引きクーポンを利用するのが最適ではなくなってくるので。もしそんなことがあれば DP でもしてすべての可能性を考えないと答えが出せないと思う。■G 問題「Minimum Xor Pair Query」。解けてないよ。黒板に書かれている数字をプリフィックスで分類しておきたいなと思った。数字は 30 ビット幅の範囲なんだけど、たとえば 29 ビット分のプリフィックスが共通する数字が2つ以上あったら、30 ビット分のプリフィックスが共通するものがないとしたら、答えは1に決まっている。できるだけ長い共通プリフィックスを見つけることが答えを出す最初の一歩だと思った。たぶん TLE なのは次の一歩が愚直組み合わせだからなのだろう。見つかった共通プリフィックスが短いほど組み合わせがやばい。プリフィックスを取り除いて MSB を無視して再帰的にプリフィックスで分類していくのだろうか。難しい、っていうかややこしいよ。プログラムの概形が見えない。今の TLE 提出の段階でもややこしくて配列に逃げたんだけど、配列の非効率的な操作が TLE の(唯一の)原因だったら嬉しいなあ。3要素以上の組み合わせを考えることってなくない? 3要素あるならより長い共通プリフィックスが見つかると思う。じゃあ最長の共通プリフィックスを効率良く見つけるのに失敗してるな。最長の共通プリフィックスを見つける算段は立ったと思うけど、それが b ビットの長さだとすると最大で 2^b 組の XOR から最小値を見つけるのはどうすれば? SortedMultiSet があれば考えることもないけど(ないんだな)。■■■G 問題。間接的に解説を読みました。「整数集合から2つ選んだXORの最小は整列させた隣り合う数のXORのどれか」 うん、そうだね。できるだけ長い共通プリフィックスを持つ2数は隣り合う数字だね。バイナリ表現と数としての評価が繋がらなかったんだよ。あと条件を緩和する発想もなかなか出て来にくいと思う。隣接する2数の中には最長の共通プリフィックスを持つものもあるけど、そうでないものも当然ある。だけど区別する必要がないし、区別しない方が処理しやすい。■■■G 問題結果報告。■1.平衡二分探索木がない言語の頼れる味方 BIT を使ったもの>提出 #43153261 (TLE×23)。XOR した値が 30 ビット整数の範囲でどういう値をとるかわからないから BIT の実装に Hash を使っている。これはお時間やばめ。■2.XOR した値に対しては Hash 実装の BIT のままだけどクエリ先読みができる x については Array 実装の BIT を使った。あと必ず効果があるものではないけどクエリ1と2の処理をクエリ3が来るまで遅延させて同一の x に対する処理がまとまるようにした。提出 #43229129 (TLE×14)。■3.XOR した値に関して Hash BIT の代わりに削除可能なプライオリティキューを使うようにした。提出 #43229096 (AC / 2342 ms)。削除可能なヒープの実装が『アルゴリズムとデータ構造』(メールホルン, サンダース)の課題にあったと思うんだけどどう実装するのか知らなかった。だけどこの前比較可能なキーだけを突っ込めるプライオリティキューにひと皮かぶせてキーに値を関連付ける方法を hmmnrst さんの提出 #42277952 で知ったので、キーに個数を関連付けて削除とするのは簡単だった。ヒープへの出し入れは定数倍が重いので節約術(2個目以降の同一キーはヒープに入れない)としても個数の管理を役立てていきたいと思う。もうひとつ。初めて見る prepend メソッドの使用例でもあった。これは継承を操作するメソッドで、自分が持つ言葉のイメージとは裏腹に prepend されたモジュールは継承階層の末端側に配置される。反対のメソッドは append ではなくて include らしい。そちらはよく知っているが目から鱗の発見でもあった。include↔prepend なのか。prepend は Ruby が 1.9 の頃にはなくて 2.3 の時にはもうあったメソッドらしい。ところで自分の提出では prepend も継承も使っていない。オーバーライドされた親クラスのメソッドを呼び出す方法が見つからなかったからだ。具体的には PQ2#head メソッドの中で、オーバーライドしている PQ2#deq メソッドではなく PQ#deq メソッドを呼び出したかった。同名のメソッドなら super で呼び出せるけど。C++ には明示する文法があるんだけど。図らずも「(可能ならいつでも)継承よりコンポジション」を別の理由から確認する結果になった。たぶん Ruby での方法は alias だけど、あまりにも姑息なやり方だと思う。■4.これは解説を読む前に作っていた「最長の共通プリフィックスを見つける算段は立ったと思うけど、それが b ビットの長さだとすると最大で 2^b 組の XOR から最小値を見つけるのはどうすれば? SortedMultiSet があれば考えることもないけど(ないんだな)。」と書いていた頃のもの。2^b 組について愚直に全件調べているが意外に健闘した>提出 #43229224 (TLE×14)。


2023年06月30日 (金) [AtCoder] 精進。ABC146-E「Rem of Sum is Num」(青 diff)。累積和(要素1、要素1+2、要素1+2+3、……)を K で割った余りを数えておく。このとき添字を使って補正することで、要素数1の部分列がとるべき値、要素数2の部分列がとるべき値……が同一の値になるので、ある要素を始点とする部分列のうち条件を満たすものが定数時間で数えられるようになる。注意を要するのは、要素数が K 以上の部分列が条件を満たすことはないので、うっかり K 個以上の累積和を集計してはいけない。■提出 #43073937 (AC / 352 Byte / 224 ms)。全然サンプルが合わなくて、あっちを修正しこっちを修正し、一休みして考え違いを修正して、やっとすべてがあるべき形に納まったときにサンプルが合った。実装ミスが2つ以上あって根本的な考え違いまであったのでは数合わせはほぼ不可能。つらかった。


2023年06月29日 (木) 馬主。アニメ【推しの子】8話でバヌシと発音されていた。自分もそう読んでいたけど少し前にウマヌシと発音し直される機会があって、音読み訓読みを揃えるならバヌシは異端だったかもしれないなと納得していた。バヌシと読むのが自分だけではないと知って辞書を引いてみたらウマヌシとバヌシに加えてバシュまで載っていた。ただ、微妙な違いもあるらしく、明鏡国語辞典ではバシュとバヌシは相互参照の関係にあるがウマヌシはそうではない。バシュ、バヌシには「馬の持ち主」という意味しかないがウマヌシには2つの意味があって、1番目が「馬の持ち主」でありバシュ、バヌシへの参照がある。2番目がウマヌシ固有の意味で「競走馬の持ち主」とある。広辞苑にもこれら3つの見出しがあるが、やや混乱が見られる。ウマヌシからバシュ、バヌシへの参照があり、バシュ、バヌシが相互参照の関係にあるのは同じだが、バヌシからウマヌシへの参照があるところが異なっている。ウマヌシはバヌシを包含するらしいからバヌシ⇒ウマヌシは(明鏡と異なっていても)不自然ではない。バシュからウマヌシへの参照がないのが不自然。意味はウマヌシが「馬の持主。競走馬の持主」、バシュとバヌシが「(競馬で)馬の持主」とある。「(競馬で)」はまったく余計だと思う。そのせいでウマヌシとの区別が曖昧になるし、競馬ではない馬の持ち主を指す別の言葉があるとも思われないので。あ、普通に持ち主とか飼い主か。