/ 最近 .rdf 追記 設定 本棚

脳log[2024-12]



2024年12月01日 (日) [AtCoder] 昨日あった AtCoder プログラミングコンテスト2024(ABC382)について。精進を済ませてからにしたかったけど目途が立たないのでとりあえず。■A 問題 Daily Cookie. を数えるか @ を数えるかして、D を足すか引くかする。■B 問題 Daily Cookie 2。後ろから書き換える。愚直に N^2 の時間をかけて大丈夫。String の場合は検索開始位置が渡せるのでケチって線形時間にもできる。Array で検索開始位置が指定できないのはスライスを使えってことなのかなと思うけど、添字の調整が面倒くさいからやらない。■C 問題 Kaiten Sushi。配点がちょっと高い。上流にいる人が食べたいものをすべて食べてしまうという容赦ない設定は流し索麺を思い出す(Somen Nagashi)。あちらは離席して食べる時間があるけども、こちらは容赦なき総取り。人の列が減少列になるように間引いてから二分探索をしたけど、寿司の方をソートしてしまえば二分探索はいらなかった。計算量のオーダーは変わらないので実装はお好みで。■D 問題 Keep Distance。すべて出力しろと言っているのだから、愚直に列挙して間に合う制約になっている。DFS が書けますかという問題。あ、嘘嘘。愚直に列挙したら最大ケースが終了しなかったので先を見て予め列挙範囲を限定する必要があった。無駄なく列挙しないといけなかった。■E 問題 Expansion Packs。2乗の DP が許される制約(?)。解答形式が小数だからサンプルも小数で書かれているし、サンプルの2が自己ループを含むものになっているおかげでデバッグが捗って助かった。何をしたか。現在持っているレアカードの枚数 R とそこに至る確率をベースとして、今開ける1パックの期待値(=1パック×確率)を答えに足し、レアカードが R+X 枚になる確率を配っていきたい。そうすると1パックあたり X 枚のレアカードを得る確率を予め計算しておく必要がある。そうすると1パックにレアカードが0枚で足踏みしてしまう場合があることに気がつく。こういうのは具体的にどうするとうまくいくかを考えた。1パックあたり3分の1の確率でレアカードが0枚に終わるケースで考えると、1回に2分の3パック開ければレアカードが期待できる。レアカードが入っている前提ではレアカードが X 枚入っている確率は、分母(全体の場合の数)が減っているのだから最初に求めた数字より大きくなるはずで、1/(1-1/3) の式で倍率を表すと丁度いい大きさになるなと考えた。要はレアカードが0枚の場合を含む全事象からレアカードが1枚以上の全事象への分母の減少率の逆数。しかし! TLE が解消できなかった。C++ で書き直したものはループの範囲を限定したりといった小細工なしでも 39 ms で AC だったけど(#60354761)、時間内には間に合わなかった。制約に泣かされた。Ruby で通している人が1人いたので、これは泣き言なんだよなあ。■F 問題 Falling Bars。消えない回らないスライドしない、落ちるだけのテトリス。区間更新区間取得のセグメント木の最も基本的な使い方がわかりますかという問題。残念ながら区間更新ができるセグメント木を持っていないので、即座に撤退して E 問題の TLE 解消に戻った(解消できなかった)。あまりにもストレートな問題なので、区間更新ができるセグメント木を書くいい機会かなと思った。以前読んだ競プロに関する翻訳された PDF (現物をなくしてダウンロード元も不明) のおかげで、トップダウン式のトラバースが自分の実装に欠けていることがわかっている。セグメント木を最初に雰囲気で実装したときに、末端から LCA までを辿るようなアクセス方法になった。だけど遅延された(区間)更新が伝播するのは根から末端に向けてなので、ボトムアップ式のアプローチはまったく役に立たない。トップダウン式の辿り方をでっちあげて区間更新ができるものをがんばって実装したけど、TLE だった(#60355045)。もうちょっと実装を洗練させたり無駄を省いたり手抜きをしたりする余地がないか考えてみたいけど、とりあえずここまで。■F 問題。通った! 提出 #60379219 (AC / 1143 Byte / 1869 ms)。さっきの TLE で再帰呼び出しをしていた4行を直接インスタンス変数を書き換えるように変更したら間に合った。今は 0 とか max とかをあちこちに(じか)に書いてるけども、これを汎化すると TLE の危険が増すんだよなあ。今で TLE まで 131 ms しか余裕がない。あと、今回は max だからごまかせてるけど、モノイド(?)が扱えるようには気をつかっていないので、簡単な置き換えだけでは乗せられないと思う。「遅延評価セグメント木に載せられるのは、モノイドに作用と呼ばれるものを付け加えた「作用付きモノイド」です」ということも今読んで知ったぐらいなので。■■■つづけて ABC357-F「Two Sequence Queries」に挑戦しているけど、概ね合うけど合わないものもあるという感じ。そして TLE が避けられないのは間違いない。だって1点更新のセグメント木で凝ったマージをするためにブロックを与えただけでもすぐ TLE になるのだから(いわんや~をや)。必要に応じて定義していったら演算が3つになったけど、こういうものなのかな。1つ目は普通のセグメント木と同じで左右のセグメントを連結するもの。2つ目が区間に対する更新。3つ目が区間に対する更新の更新(3を足して6を足すなら9を足すことにするみたいな)。■■■E 問題。もう C++ で通しちゃったし TODO として覚えておけないだろうから他の人の Ruby での提出を見た。みんな require 'numo/narray' していた。うん、それはね、Rust も Crystal も numo/narray もローカルに、古い Windows にインストールできなくて試行錯誤できないから捨ててるんだ。あきらめがついてすっきりした。■E 問題。あきらめたと書いたそばから通っちゃったね。提出 #60486554 (AC / 355 Byte / 1966 ms)。しょうもないなあ。コードテストで判断すると、制限時間が3秒なら TLE だった提出も TLE ではなかった。TLE と AC の違いはアルゴリズムの違いではなく、スクリプトの記述の違いでしかない。だからしょうもない。でも飛び道具なしのピュア Ruby でも不可能な制約ではなかったと自分で証明してしまったんだよな。なんだよくやしいなあ、あきらめがつかないじゃないか。■■■@2024-12-18 ABC357-F の精進について 20241218 に書いた。ついでに書くけど、さっき通ったと喜んでいた ABC382-F への自分の提出 #60379219 (AC) にはバグがある。61 行目のセグメント木の初期化サイズが間違っているので、N が W よりたっぷり小さいときエラーが出る。


2024年12月02日 (月) 訪問予定のお客さんから予定していた時刻には帰宅できていないと連絡がありました。帰宅予定は X 時 15 分だそうです。どうしますか?■自分は 15 分を過ぎた頃に伺いますと言って 20 分に到着する予定を立てるんだけど、15 分に行くと言って 15 分に到着する予定を立てる人や、5分10分前に到着して待つ(待たせない)ことを良しとする人ばかりで同意を得られない模様。■自分の行動背景。15 分に行くと伝えたら、15 分ではなく 15 分までに帰宅していなければいけないと焦らせてしまうのではないかと考える。そのように考えない人のことを自分は気にかけるつもりがないし、その必要もない(だってそのようには考えないのだから)。遅刻した人を待ち構えることについて。それは待たせてしまったことに負い目を感じさせる行為だと考える。「待った?」「今来たところ」の様式美が自分は好きだ。自分の行動の失点は何か。すでに最初の予定時刻を過ぎているのに、さらに帰宅から5分かそこら待たせてしまうかもしれない。しかしさっきから書いているように、人を待たせているのに自分は待ちたくないというような厚かましい人のことを自分は気にかけるつもりがない。善良な人のことを気にかけている。ただし、そのための最適な行動が何かについては自信がない。■ここに書かれていることから自分という人間について3点読み取れることがある。というか結局自分のことしか書いていないといえる。それは、焦ること(時間に追われたくないこと)、負い目を感じさせられたくないこと(わがまま!)、15 分に帰ると言ったら真実 15 分にしか帰れないこと(方便としての嘘がないことそれを想定しないこと)。5分に帰るつもりで 15 分には帰れると伝えるのは別の話(これは嘘ではなく条件を緩めた本当のことなので)。ここからもう1点読み取れること。自分は善意からであれ利己心からであれ、嘘をつくコストを一切負担したくないということ。そのコストを負担しきれないことをすでにいやというほど知っている。話がそれるけど、「いやというほど」を漢字にすると「否という程」の一択になるらしい、ATOK(+広辞苑) によると。「嫌」じゃないんだ、知らなかった。イヤとイナヤの違いなんてわからないからしかたないね。あ、5つ目。今こいつ自分のことを善良な人間とイコールで結びやがった。


2024年12月07日 (土) [AtCoder] 今日は大和証券プログラミングコンテスト2024(ABC383)があった。解ける問題を順当に解いてもぞもぞしたレート変動。これを冴えない結果という。ABC問題の配点がそろって上振れしているのを警戒していたのだけど、手が忙しいという点を評価しての上乗せだった。■A 問題 Humidifier 1。ヒュ…ミ…ディ…? この感じの読めなさは湿度に関する単語だということを知っている。問題文に加湿器とあるからそれだろう。もうひとつ読めない書けない発音できない単語といえば血圧計で、スフィグモマノミターという。どうでもいい話。こういう考えに邪魔されながら問題を解いてるんですよ。今日の T はソートされています! 変数 t(ime) と v(olume) を持って順番にシミュレートする。■B 問題 Humidifier 2。3連星の2つ目。全探索の愚直カウントで OK な制約。問題をよく読まないから床の座標だけでいいのに机の座標を用意したり削除したりしていた。■C 問題 Humidifier 3。オフィスの広さがさっきの万倍になりました。1メガの入力文字列はなかなかの大きさ。これって何アールかな、何ヘクタールかなとどうでもいいことをまずは考えていた。たしか 100M×100M が1アールだったと記憶している。単位の換算表が書かれた青っぽい下敷きを学校で使っていた。ミリバールはもう載っていなかったと思う。BFS をやります。■D 問題 9 Divisors。D 問題らしい問題。E より難しくて 40 分かけた。約数の数が奇数というのが最初のポイント。これって平方数かなと考えていくつかの数について約数を列挙してみてその通りだと思った。N 以下の平方数で約数が9個のものを Integer#prime_division で見つけようとしたけど、最大ケースのサンプルが通らない。200 万回繰り返すには素因数分解のコストは高すぎるらしい。もうちょっと考えてみる。約数の数が9個ということは、素因数分解したあとの形が p1*p1*p2*p2 になるものしかないのではないかと考えた。約数の数っていうのは素数ごとの (指数の値+1) をかけあわせたものなので、9になるのは (2+1)*(2+1) だけなのではないかと。ここでも平方数だ。2乗なのか4乗なのかもうわけがわからないよ。素数の2乗を列挙するだけで済むので時間は間に合うようになったけど、最大ケースのサンプル2が合わない。10 いくつかだけ少なく出る。時間オーバーの素因数分解解法の答えは合っているので、9=(2+1)*(2+1) の部分で漏れがある。9=(8+1) のケースが漏れていた。2乗4乗の次は8乗だってさ。もうわけがわからないよ。こうなるとサンプル1の嫌らしさに気がついてしまう。最初の8乗数が 256 だから、サンプル1の N=200 は絶妙に少ない。そんなこんなで 40 分間たいへんな思いをした。コーディング部分は列挙して二分探索して足すだけで何も難しくない。Ruby で 307 ms の提出があるなか自分のは 1004 ms かかっているあたり、考察がまだ甘いのだけど、もう考えたくないのだ。■F 問題 Diversity。配点が E より 25 点だけ上なのでまずは F 問題を読んだ。DP。パラメータが多い。制約は N が 500 以下な点を除いて甘くない。特に色の種類が N 以下と全く限定されていないので、どの色がすでに選択済みかというビットフラグを状態のキーにはできない。色の扱いが問題だと思った。色ごとに DP をして価格と効用がそろって上昇するように商品グループを列挙しておいて、そののち価格と効用についての DP をするのかと思った。だけど後段の DP で扱う商品群がどれだけの大きさになるのかわからない。商品を組み合わせたものをさらに組み合わせようとしている。ここらで E 問題へ。■E 問題 Sum of Max Matching。頂点数も辺の数も少なくないグラフ。パスに含まれる辺の重みの最大値を考えるということは、回り道をしてでも軽い辺を使うべきだということ。UnionFind で軽い辺から使って徐々にグラフを拡張していくのかな。特に短くない2つの数列 A と B が与えられて、その組み合わせの最小値を答えるという。各頂点は A 数列か B 数列のどちらかに含まれるか全く含まれないかで、どちらかに複数回現れることもある。グラフを拡張しながら連結成分内で A-B ペアを作ろう。具体的なペアを考えるには A,B 数列は長すぎるけど、連結成分ごとに余っている A(B) 数列要素の数を記録しておくだけでよい。A 数列の要素と B 数列の要素を貪欲に消費して損をしない。F 問題に寄り道していた時間を含めても提出まで 21 分だから、D 問題より早い。これは不思議のないことで、UnionFind を使うからクラスカル法を使うからという理由で E 問題に配置されている問題は、のーみそこねこねな D 問題より型にはまっていて解きやすいことがある。今日は得をしたけどこれとは逆に、LazySegTree を使うからという理由で F に配置されている問題は、diff が低い割に配点が高く、だけど Ruby で参加している自分は TLE にならない遅延セグメント木の実装を持っていないので撤退するしかないということで大損をする。くやしいね。■F 問題。色で商品をソートしておいて、色が切り替わるところで DP テーブルを切り替えることにして、あとは普通に DP をするだけだったのだろうか。DP は1回だけで良かったんだろうか。どんだけ考察が早くても残り 20 分で実装できる DP ではなかったけども。■■■たぶんだけど、1アールに関連して覚えている 100 という数字は、掛ける前の数字ではなく掛けたあとの数字ではなかったか。つまり、10M×10M = 100M^2 = 1アール なのではなかったか。そうすると1ヘクタールに関連付けて覚えている1万という数字が、100M×100M = 10000M^2 = 1ヘクタール ということになっておさまりがいい。自分の生活に一切関わってこなかった知識なのでわざわざ調べませんけども。■C 問題に関連して多始点 BFS というワードを見かけたので調子に乗ったことを書くんだけども、始点の数を区別することに意味がありますか? 移り変わるキューの中身をのぞいてみる。キューの中に始点(距離ゼロの頂点)が詰まっている状態がスタート。そこから距離ゼロと1の頂点が詰まっている状態、距離1の頂点が詰まっている状態、距離1と2の頂点が詰まっている状態、距離2の頂点が詰まっている状態、距離2と3の……状態が次々と現れる。距離ゼロの頂点だけが唯一でなければいけない理由はどこにもないと思うんだよ。同じ距離の頂点が1個だろうが複数だろうがキューの中で整然と列をなすからプライオリティキューなしで BFS が成立している。■精進。F 問題。昨日「どんだけ考察が早くても残り 20 分で実装できる DP ではなかったけども」と書いたわけなんだけど、ARC189 が終わったあとで書き始めたら 11 分で完成したよ。提出 #60595097 (AC / 253 Byte / 1030 ms)。ひとひねりあっただけで初歩的な DP でしたね。ひとひねりでてきめんにやられてしまうところに応用力のなさが現れている。BFS の始点が複数になってやられるのと同じことだよ(調子に乗ってごめんなさい)。


2024年12月14日 (土) [AtCoder] 今日はトヨタ自動車プログラミングコンテスト2024#12(ABC384)があった。まずまずのでき。ABCDE のふりかえりと F の精進を。■A 問題 aaaadaa。言われた通りに。正規表現を使った置換だと一発だったのかな。操作が否定で定義されていて結局何をするのかよくわからなかったので言われた通りに。■B 問題 ARC Division。言われた通りにシミュレート。■C 問題 Perfect Standings。なにげにやっかいなのが同点での名前辞書順。得点と名前のペアでソートしてから名前を出力すれば良かったんだけど、名前に変換する前の数値の状態でソートして間違えた。一番複雑そうなサンプル3の出力だけ比較して一致することを確かめていたのだけど、厳密な比較をサボったそれ以外の2つのサンプルがどちらも合っていなかった。1ペナ。■D 問題 Repeated Sequence。数列を前から順番に処理しながら現在の要素を終端とするすべての累積和を列挙することができる。あとは N 項の和の余りをとったり、A 数列を2サイクル処理したりすることに注意する(数列を1周以上するケースや、N=7 のとき、A6+A7+A1+A2 のような累積和を対象に含めるため)。■E 問題 Takahashi is Slime 2。高橋くんスライムに隣接するマスをプライオリティキューに入れて、最小のものから取り出して併合する。■精進。F 問題 Double Sum 2。えっとね、つい最近、この問題と全く同じように数列を 0/1 で分類して数え上げる問題が出たと思うんだよね。時間外に考えてみてそれでも解けなかった問題なのでこの F 問題も解けないのがある意味当然だったのだけど、今日は時間はオーバーしたけど AC までたどり着けた。まず、trailing zeroes の数が異なる要素同士の和の f 値は簡単に求まる。一致するもの同士をどうするか。末尾の 1+1 の繰り上がりを考慮に入れたうえで、その次のビットを見る。一致していればそのビットが1に確定し、f 値が計算できる。異なっていれば、繰り上がりを考慮したうえでさらに次のビットを見る。提出 #60779596 (AC / 882 Byte / 608 ms)。数列を1つとる F 関数をまず書くんだけど、それにくわえて数列を2つとる FF 関数が書けるかどうかが AC に至れるかどうかの分かれ目だった。FF 関数に2つの数列をビットで分類した4つの変数 a0,a1,b0,b1 がある。10 通りの組み合わせのうち、a0×b0, a0×b1, a1×b0, a1×b1 の4つは答えに計上するけども、a0×a0, a0×a1, a1×a1, b0×b0, b0×b1, b1×b1 の6つは答えに計上してはいけない(これらはすでに F 関数で答えに計上されているので)。このように、0/1 で分類して 0×0, 0×1, 1×1 の組み合わせについて答えを計上する F 関数と、似ているけど異なる FF 関数がなかなか書けなかった。F 関数は FF 関数を呼ぶけど、FF 関数は FF 関数を呼ぶ再帰関数なので、FFFF 関数を定義する羽目には陥らないということも見通せなかった。F 関数の要求からとりあえず FF 関数を定義してみて、FF 関数が要求するものは FF 関数だったのでめでたしめでたしという流れ。■これかな? PAST18-K「2で割り切れる回数」。解けなかったんだよね。シグマの範囲がちょっと違うだけで同じ問題だ。今なら解けるだろうか。ちなみに、Ruby で 81 バイトで解けるらしいです(#60224722)。通った。提出 #60784486 (AC / 650 Byte / 301 ms)。長さから判断するに明らかに不器用な数え方をしているけども、今日の F 問題と同じ構成で解けた。F 問題も K 問題も制限時間が4秒5秒なんだけど、想定解法ではない? 1秒しかいらないよ。


2024年12月18日 (水) [AtCoder] 精進。ABC357-F「Two Sequence Queries」。20241201 で初めて実装した区間更新ができるセグメント木のテストを兼ねて挑戦していた。提出 #60878023 (AC / 2167 Byte / 3561 ms)。TLE を回避する高速化にあたっては先達の提出(#54387396#54497837)を大いに参考にした。最も重要なアイディアは Array ではなくクラスを使うことによる自己書き換えだと思った。これによりオブジェクトの使い捨てが抑制できる。1点更新のセグメント木で凝ったマージをしようとするとすぐに TLE になるのもおそらく原因は同じで、1セグメントあたり配列に3要素も4要素も値を保持し、更新のたびに新しい配列を作成するコストが重すぎるのだろう。具体的な数字やどこで読んだかは忘れたけど、配列のサイズが4つや5つを超えると要素を Array オブジェクト(を表す内部データ)に埋め込む最適化もできなくなるだろうし。■それほど重要ではないけど TLE と AC を分ける細かい選択がいくつもある。hmmnrst さんの複数の提出の差分を調べると、mod の値はインスタンス変数に保持するより定数に保持する方がいいみたい。多重代入は1行ずつ分けるのがいいみたい。多重代入が重いのは知ってるし、仕様の変更でさらにちょっと重くなるらしいのも知ってるけど、冗長なのを嫌って自分は多用するんだよね。だけど多重代入が TLEAC を分けているのを実際に見ると、好き嫌いではなく選択の余地はないのだなと理解してしまう。多重代入を分解するときは代入前の値を参照しているつもりで代入後の値を参照するバグを仕込みやすいので注意が必要。過去に何度も苦しめられたし、今回も、自分の AC 提出の 93 行目、Seg#apply メソッドで @ab 変数への代入順がイレギュラーなのは、最初からそうだったわけではなく、@a、@b 変数への代入のあとでは値が狂ってしまうというバグとデバッグの結果そうなっている。同じメソッドのことだけど、2つのアクセサを3回ずつ呼び出して値を参照していたのを、1回ずつに減らすためにローカル変数に受け取っている。プロファイラ(ruby-prof)で呼び出し回数と消費時間が上に上がって来ていたから対策した。自分のスクリプトの Seg クラスと Op クラスは扱いから考えて Struct にしたかったのだけど、数百 ms 以上遅くなるみたいだったのでクラスにしている。Seg#concat メソッドの呼び出しはレシーバと第一引数を同じにすることで容易に Seg#from メソッドで置き換えられるのだけど、これもパフォーマンスの劣化が無視できなかったのでスクリプトの冗長さをあえて受け入れている。Seg#from メソッドは引数で自分を完全に上書きするあたりコンストラクタと同じなのでインスタンスではなくクラスに生やしたいメソッドなのだけど、パフォーマンスのために自己書き換えが目的なのでしかたなくインスタンスメソッドにしている。ST#set メソッドと ST#get メソッドは8割が共通なのでマージできなくはないけど、わずかに遅くなるのとそうすべきなのかがわからないので分かれたままにしている。%998244353 をどこに置いてどこに置かないかも制約から計算してよくよく考えた結果のこと。実は自分のスクリプトは1秒以上も差をつけられて遅い状態が1日以上続いていたのだけど、この要不要の見積もりを誤っていたことが原因だった。パフォーマンス特性がこんなにも違うのだから、Bignum を Integer の背後に隠すのはどうかと思うんですよ。理想と現実にギャップがあるとき、現実を隠蔽することに利があるとは思わない(自動的な昇格ではなくクラスについて書いている。デバッグがしにくい)。他にも子要素の添字を計算する際の +1 を省くために内部データを 1-indexed にしたりという、針の穴を通すような細かな積み重ね抜きでは Ruby で AC を取ることができない。正直言ってコンテスト本番ではこの手の問題(遅延セグメント木、N>300 のワーシャルフロイド法、N=5000 の2乗の DP など)はさっさと投げ捨てて他の問題を解いた方が良い。なお、できる人は Ruby を投げ捨てるもよう。■記事を閲覧履歴から発掘した。「Ruby 1.9 では RVALUE に「embed」という考え方が導入され、文字列や配列などデータのうち、サイズの小さいモノは別途 malloc するのではなく RVALUE の中に埋め込んでしまうことで、 malloc & free のオーバーヘッドを削減でき、またキャッシュの局所性を高めることができます。」「ちなみに Array なら 3 つまで embed で扱います。」(Ruby は String をメモリ上でどのように扱っているのか? | IIJ Engineers Blog)■最初期の提出を見てみると、出力用の第3引数を渡すことで配列を使っていてもオブジェクトの使い捨ては抑制できていたみたい。それでひどい TLE なのだから、クラス化による最大のメリットは [] メソッドを使わないインスタンス変数へのアクセスなのかもね。


2024年12月22日 (日) [COSMOS] メモリ 2GB×2 を抜いて 8GB×2 を差した。4GB×2 と合わせて 24GB になった。ところで、このメモリって DDR3 なんです。1枚 1300 円だったけど、いつまで手に入るかがもうあやしい。DDR5 が差せるマザーボードを選びたいけど、載せたい OS がない。


2024年12月24日 (火) [AtCoder] 精進……失敗! 先週末あった ABC385-F「Visible Buildings」。傾きでソートしながらどんどん更新していくような解法を検討していたのだけど、全然違った。「Fはまず何も考えずに二分探索を書いた」という人がいるが、さっぱりわからない。別の所で「原点から全てのビルを見られなければ、左から順に左隣のビルの陰にならないように高さを上げていきます」と読んで、嘘だぁと思いながら、なんで左隣のビルを見るだけでいいのかを考えていた。こんな感じ。3つのビルがあって原点に近い方から A、B、C とする。AB のてっぺんを繋いだ直線を C まで伸ばしたときの上下を考える。C の上空を通り過ぎるとき、答えに近いのは BC のてっぺんを繋いだ直線なので A を起点として考えるのはおしまい。C の側面に触れるとき、AB を結ぶ直線を保留にしたまま B を起点にした直線について考える。いずれにしろ A と C が関係を持つことはない。A と C を結ぶ直線が A と B を結ぶ直線よりも答えに近いとき、B と C を結ぶ直線の方がより答えに近いのよね。だから A と C の関係は答えに寄与しない。本当に? わかりません。実装を始めたらサンプル2と3の違いが難解すぎて手が止まった。共有点を持つとき、見えないんだってね。AC のてっぺんを結ぶ線に B のてっぺんが触れるとき、A から C が見えない。提出 #61050894 (WA×1 / 320 Byte / 1329 ms)。1ケースだけ通らなかった。誤差が大変というのを見かけていたので答えを出力するとき以外全部整数で計算したけど、1ケースだけ合わない。どうしたらいいのかもうわかりません。■A 問題 Equally。全部同値か、どれか1つの2倍が3数の和か。■B 問題 Santa Claus 1。シミュレート。各家1回ずつしか数えないので @ を通過したあと . に書き換えた。■C 問題 Illuminate Buildings。難しかった。実装で2つ間違えてペナルティは4回出した。AC が出たのは終了5分前。2乗が通る制約だからと愚直に書いて、念のために最大ケースでテストしたら TLE になりそうだった。愚直というのは始点を固定して全ての間隔を試すというもの。よく考えると右側にある同じ要素を何度も参照するから2乗では済んでいない。そこで間隔を中心に考えることにした。1つおき、2つおき……の各数列について、同じ要素が最長でどれだけ続くかを数える。間隔が S なら長さが N/S 程度になる S 本の数列を考える。間隔の選び方がおよそ N 通りだからこれで O(N^2) になる。ここでも罠があって Array#values_atEnumerable#chunkInteger#step を組み合わせた解答が予想よりも時間を食っていた。Enumerable#each_cons には大きい数を引数にしたとき尺取りの計算量 O(N) では済まないらしい罠があると思ってるんだけど、ここにもあったのだろうか。手続き的に書き直して最初に提出するまでに 15 分かけた。そこからペナルティを重ねるんだけど、最後まで見つけられなかったバグは N=1 のケース。間隔に注目したせいで単一要素のケースが漏れて nil を出力していた。■D 問題 Santa Claus 2。どうやって計算量に対処するか悩む。たとえば BIT を使えば各行、各列のどこに家を置いて、どこから家を取り除くかは自在に(対数時間で)できる。あるいは行ごと列ごとに移動区間と家の並びのソート列があれば並行してスキャンしていくことができる。どちらも行データと列データに重複して登録される家を二重にカウントしないために同期が必要。これは行に基づいて得た訪問家リストと列に基づいて得た訪問家リストを最後に線形時間でマージして出力すれば良い。ということを考えてから必要なデータ(行ごと列ごとの移動区間)を集めて整理していった。そうすると家を中心にして考えたとき、行データと列データを参照して二分探索をするとその家が訪問されるかどうかがすぐに判断できるなと気がついて、後半の実装がちょっとだけ楽になった。BIT はいらないし、ソート列の並行スキャンもいらないし、答えのマージもいらない。ランタイムエラーでペナルティを1回出したのは N×N のグリッドを想定していたせい。D の提出をするときに C が WA になっているのに気がついて、C のデバッグをしているときに D が RE になって、C のデバッグを優先したけど結果的にデバッグしやすかったのは D だった。いっときは C をあきらめて E 問題を読んだほど。■E 問題 Snowflake Tree。木 DP かなと思って実装を始めたら親方向の情報が欲しくなって全方位木 DP だと気がついて、残り 10 分での実装をあきらめて C のデバッグに戻った。C は終了5分前に、E は終了 10 分後に AC。最も簡単なタイプの全方位木 DP だと思ったけど、もっと簡単に中心を全探索して間に合う計算量だったらしい。頂点数が N でそこから辿る辺の数が両方向で 2×(N-1) だから間に合う。しかし気付けない。だって解けるんだから。それで時間が足りてればね。■C のミスと D の重さにやられて E を通す時間がなかったのが悔やまれる。F はどれだけ時間があっても無理なので期待も諦めもない。