/ 最近 .rdf 追記 設定 本棚

脳log[2023-10-09~]



2023年10月09日 (月) [C++] 「Dはmapを舐めている途中に要素を追加したくなる。迂闊なことをするとイテレータが無効化されると思い、事前に入っていなかった値はスキップするという実装をしたが、リファレンスを見るとinsertは既存のイテレータを無効化しないらしいので気にしなくてよかった」。まさにこれ。どこかでみた誰かの解答が range-based for の中で要素を増やしているようで、怖いことをしているなと思っていたのだけど、そうか、許されていたのか。


2023年10月08日 (日) [AtCoder] 今日は ARC166 があった。ABC で惨敗するなら(昨日のこと) ARC に慰めてもらえばいいじゃないの精神で参加してきた。ARC がそんな風にやさしかったことは片手で数えられるほどしかないのではあるが。自分のすべての提出。パフォーマンスもレイティングもしらないよ。A 問題が一生合わへんかった。WA×6 で AC なし! 合間に読んだ B 問題は特に考えることもなく実装を始めてスムーズに AC になったけど、それでも 40 分かかってるのはどこに時間を使ってるんだろう。いや、40 分には A 問題に見切りをつけるまでの時間が含まれている。ファイル(なぜか ABC166_b.rb32 だった)のタイムスタンプによれば作成から 20 分で完成している。すこぶる順調。提出 #46387014 (AC / 507 Byte / 402 ms)。関数を3つ組み合わせてなかなかきれいに解けた。やっていることは組み合わせと並べ替えの総当たり。X 数列の1つの要素を lcm(a,b,c) に合わせる、2つの要素を (a,lcm(b,c)),(b,lcm(a,c)),(c,lcm(a,b)) に合わせる、3つの要素を (a,b,c) に合わせる。A 問題のことは知らない。解けないはずがないんだけど合わない。マルチテストケースは正答との距離が計りにくい点が難しいよね。1ミスがあるだけでもほとんどのテストケースで中に複数含むケースのどれかがそのミスを咎めて WA になるということがありうる。


2023年10月07日 (土) [AtCoder] 今日はユニークビジョンプログラミングコンテスト2023 秋 (AtCoder Beginner Contest 323)があった。結果は見てない。自分のすべての提出。ABCD の4完。E 問題で撃沈。終了後に読んだ F 問題が簡単であまりに残念。崖であってほしかった。ではふりかえり。■A 問題「Weak Beats」。2進数として解釈して特定の値と比較すればいいかと考えてみたけど、偶数桁だけを見ろと言っているのでしかたなく愚直に書いた。■B 問題「Round-Robin Tournament」。添字を含めたペアでソートする。既定では昇順にソートされるので勝利数のマイナスを比較のキーにしてもいいけど、敗北数を数えるとひと手間がなくていい。■C 問題「World Tour Finals」。この前あった World Tour Finals 2022 にちなんだ問題。制約は小さいし愚直にやるだけなんだけど、意外に難しかった。何がって、すでにトップの人の扱いが。でもボーナス点が最低得点未満だということで、総合得点はそれぞれのプレイヤーに固有のユニークな値になる。何も難しいことはなかった、けど 18 分かけた。■D 問題「Merge Slimes」。サイズが倍々で、数は半分半分。貪欲に可能な限り合成を繰り返すことでスライムの数は最小になる。それを効率的に数えられますかという問題。数が半分半分になっていくから操作回数は対数のオーダーになる。愚直に操作をシミュレートして良さそう。操作した結果が既存のサイズのスライムになることがあるから、操作はサイズの昇順に行いたい。プライオリティキューを使いますか? 「キューを2本持つことでプライオリティキューを使わないでもソート済みの状態を保ちながらキューを伸ばせる場合があるというのは、AtCoder について書いた2番目の日記に書いてある」のでそのように。■E 問題「Playlist」。期待値。サンプルの1を頼りに愚直解は求まった。曲を3曲再生するパターンが N^3 通りで、そのうち X+0.5 秒後に曲1が再生されているのは 1+3+3 通り。だけど O(XXN) は通りませんよ。■F 問題「Push and Carry」。非常に拍子抜けする問題。倉庫番。ただし壁はなし障害もなし。位置関係を整理してささっと数えるだけ。最初の提出は変数名の1文字タイポで WA×2 だったけど、3分で修正できた。F 問題が崖でないと救われないなあ。E 問題がうらめしい。でも Ruby の提出を見ると F 問題を解いてる人は E 問題も解いてるみたいなので、E 問題を解けないのが悪いのだな。E と F を両方とも解けてないのが悪い。■■■翌朝。E 問題。最初に考えた解法は O(XN) で間に合う感じだった。でもサンプルの1で 1+1+1 通りは数えられても 1+3+3 が数えられなかった。どうやって ×N するか考える過程で O(XXN) になって TLE が避けられなくなった。ところでね、スタート地点を N^X にして遷移1回につき ÷N のペナルティを払うというのはどうか。あとで答えが合うかたしかめる。■D 問題。みなさんプライオリティキューなど使わずに答えを出している(使うと log が2乗で TLE になるというのはある)。その中でも提出が早めで一番実行時間が短いもの(tinsep19 さんの)を雰囲気だけ読んだけど、その解釈らしきものに翌日になって思い至った。考えなくても湧いてくるなんてお得だ。スライムのサイズの trailing zeroes を処理することで途中で合流するスライムを予め1つにまとめられるのではないか。操作の巻き戻し。操作回数は答えの一部ではないから巻き戻しはタダで行える。あとはソートもいらず初期サイズごとにカウントするだけ。これもあとで答えが合うかたしかめる。■E 問題。どの日記を読んでも特別どうということなく答えを出している様子。最初の O(XN) 解法で分母が1つだと考えたのが間違いだったのかも。つまり、サンプルの1で 1+1+1 通りを数えたと書いたけど、それは (1+1+1)/(何か) ではなくて、1/27+1/9+1/9 だったのではないか。これを解法2としてあとでたしかめよう。以前も書いたけど、確率・期待値の問題って、こうすれば答えが出るという筋道がさっぱり読めないところが弱い。あれこれやってみて正解を導き出す解法にぶつかるのを待っている。当てもんをしている。初心にかえれというのは簡単だよ。全通りを並べてみて、そのうちのいくつが該当するかを数える。その割合。実に簡単。■順番に3つ。□提出 #46376658 (AC / 273 Byte / 393 ms)。F 問題。最初に N^X 通りを計上しておいて、遷移ごとに ÷N のペナルティを払う。□提出 #46376189 (AC / 156 Byte / 193 ms)。D 問題。最初に逆操作をしてスライムをグループごとに統合してから個数の1のビットを数えて合計する。□提出 #46376313 (AC / 233 Byte / 465 ms)。E 問題。ふつーの期待値 DP だった。1/N の可能性を足し上げていく。X を超える遷移については曲1の場合だけ記録するようにして、その他の曲に関しては X 未満までしか遷移させなかった。そうすると DP 配列の X を超える部分に答えが入っている。いやー、これが解けないならどんな期待値の問題も解けないよ。イチからやり直した方がいいよ。早め6完の問題セットだったよ(だが4完)。■■■いま気がついたんだけど、E 問題は期待値の問題ではないよね。確率を問われている。何回期待値と書いただろう。気がつかなかった。一応概念は知っているつもり。リスクに相当するものだよね。(脅威の大きさ)×(発生確率)。(賞金額)×(当選確率)。でも確率と期待値を区別せずに問題を解いているってありえることなの? しかも解けてなかったし。


2023年09月30日 (土) [AtCoder] 今日は ABC322 があった。コンテスト成績証自分のすべての提出。例によって ABCDE の5完。まあまあ早め。つまり 30 分余らせて F 問題が解けなかったと。持っていないデータ構造が必要な気がしたんだけどどうかな。ではふりかえり。■A 問題「First ABC 2」。strstr が使えますかという問題。■B 問題「Prefix and Suffix」。if 文が嫌いなので場合分けではない方法で出力値を得たいけど、両方とも該当するときにゼロを、両方とも非該当のときに3を、というのがもう面倒くさくて考えることをやめたくなる。せめて逆なら。■C 問題「Festival」。A 数列の先頭を適当に弾いていきながら1から N 日目までループを回す。A 数列の末尾が必ず N だということで番兵も添字の範囲チェックもいらなくて助かる。■D 問題「Polyomino」。E 問題と diff が逆転して水色だった問題。自分も E 問題より長い 37 分の時間をかけている。とても面倒くさい問題だった。面倒だけど愚直にやるだけ。嫌いではないよ。箱入り娘(20210121)とかカレンダーパズル(20210527)のソルバーを書いたりしてる。でもこういうのはじっくり時間をかけて整理して書きたい。■E 問題「Product Development」。この DP が緑 diff って怖い。AtCoder の参加者が怖い。DP 配列を用意しようとして困ってしまった。K の上限は高々5だけど、5次元配列はなかなかにつらい。「DP だったんだけど、人類が扱うには次元が高すぎるのではないかな? 自分には無理」と書いたときの次元数は3だった。さらに次元数(K)が入力により可変なのが輪をかけてつらい。添字を K 桁の P+1 進数にエンコードして1次元化することでなんとか扱えるようになったけど、それは人間の理解を助けるための余計な処理であって、460 ms の提出 #46108878 (konayuki さん) より 1.5 倍くらい遅くなってしまった。


2023年09月29日 (金) [AtCoder] 精進。この前の ABC321-F「#(subset sum = K) with Add and Erase」。当日は残り 15 分のうち 10 分を使って愚直 TLE 解を書いていた。5000^2 くらいの計算量だと思ったから書いたんだけど、たぶん3乗だったから TLE なんだろう。そうすると1つの DP 配列を使い回して巻き戻しができないといけないなと消去法的に考えて残り5分で修正を図ったけど、なぜか場合の数に負の数(modulo に近い数)が出てきて時間切れ。今週はこの問題をつねにうっすら考えていた。何を引き過ぎているのか。ところで、ひとつ重要な観察として、DP の遷移の順番には意味がない。+a、+b、+c と操作したものと、+c、+b、+a と操作したものでは結果が同じ。だから -c で巻き戻すものというのは、直前に +c した操作を取り消すものだと考えて良い。何を引き過ぎているのかもうわかりましたね。さっきリンクした TLE 解の 13-15 行目において DP の遷移を表すループ((K-x).downto(0){|i| ds[i+x] += ds[i] })があえての降順なのはなぜか。今ループの遷移結果に対して再帰的に遷移操作を施してはいけないからだ。+x 操作が 0 を x に移すのも x を 2x に移すのもいいが、0 から移ってきた x を 2x に移すのはやりすぎ。DP 配列を遷移回数と同じだけ用意して退屈なコピー操作を書くのもひとつの方法だけど、ループを逆順にして書き込む前に読み込むことを確実にするのも手。巻き戻しをするときはそのような工夫が通用しない。すでに遷移が済んだあとの場合の数から、最終ループで加算した場合の数を求めて引き算しなければいけない。■提出 #46049349 (AC / 276 Byte / 1183 ms)。なんというか、きれいにはまって驚くんだけど、ループを昇順にすることで巻き戻しと再帰的な影響を取り除くことが同時にできる。■「この手の場合の数は母関数です。y^K の係数が場合の数です」というような内容をいくつか読んだけど、残念なことにそれはすでに武器を持っている人にしか役立てられない情報なのだな。(1+x)^N なら x^K の係数は自分でもコンビネーションで求められるけど、今回のように x の部分が何乗なのかいろいろな場合に、いろいろ掛けた結果 x が K 乗になるものがいくつあるのか知りたいとなっても、それこそ DP をして求めるしか方法を知らない。そしてそれができたら今回の問題は解けている。つまり「形式的べき級数解説 | maspyのHP」という武器を持っていない人間にとって言い換えはただの言い換えであって答えには近づいていない。残念だなあ。理解できないんだもんなあ。


2023年09月23日 (土) [AtCoder] 今日はサントリープログラミングコンテスト2023(AtCoder Beginner Contest 321) があった。コンテスト成績証自分のすべての提出。ABCDE の5完遅解き。今回のネックは C 問題と E 問題だった。それぞれ 21 分と 56 分(+ペナ5分)かけている。これは考えていた時間ではない。ひたすらに実装が下手だった。脳内イメージをコードにするプロセスがバグっている。ではふりかえり。■A 問題「321-like Checker」。はい。■B 問題「Cutoff」。制約が 100 以下とささやかなので素直に愚直解を書く。■C 問題「321-like Searcher」。K に1以上以外の制約がないことの意味をしばらく掴みかねた。だから文字列で答えを構築する必要があるのかなとか考えていた。そうとしても読み込む前に K が 64 ビット整数だとは知らせてほしい。なぜなのか。問題の不備なのか。最大の 321-like Number が 9876543210 だというだけのこと。全列挙すればいい。その列挙に 21 分かける自分のおつむに絶望するんですよ。言い訳をするなら、0 と空文字列が現れないようにきれいに列挙することができなかったという事情がある。無視すればいいだけなんだけどね。他所で読んだんだけど、321-like Number とは "9876543210" の部分文字列のことだった。そう捉えられたらもっとスムーズにコード化できただろうなあ。2の 10 乗通りの部分文字列に 0 と空文字列が現れるのも自然なことだとすぐわかる。■D 問題「Set Menu」。考える要素はどこにもない。ソートして P 以上になるペアを弾いて、残っているものの総和と P をいくつか足す。……という処理を一方の数列の各要素ごとに他方の数列に対して行う。両方の数列をソートしておくなら二分探索はいらない。AC まで4分。■E 問題「Complete Binary Tree」。セグメント木をイメージすればいい。ただし要素数が2べきに揃えられてはいない。K の上限が N-1 になってるけど、実際にありうる上限はセグメント木の高さ×2 なので 2log(N)≦2log(10**18)<120。10 万あるテストケースごとに K を変化させながら木を辿って構わない。あるノード k があるとき、これの d 階層下のノードは 2**d*k 以上 2**d*(k+1) 未満の番号を持っている。N と比較すればいくつのノードが該当するかは簡単に数えられる(はずだった)。与えられた X から根までさかのぼりながら、直前までいたノードと逆のノードの下にいくつ当てはまるノードがあるかを数えていく。根に辿りつくまでのあいだに K がゼロになったらそのノード自身をひとつ数えて終わりにする。やりたいことは最初から明確で大枠はすぐにできあがったけど、細部がひたすらバグっていた。最後まで残っていたバグは、「(根までさかのぼりながら、)直前までいたノードと逆のノードの下にいくつ当てはまるノードがあるかを数えていく」処理における「逆のノード」。直前までいたノードが N 以下なのはたしかだけど、逆のノードは必ずしもそうではない。d 階層下にばかり気を取られていて完全に盲点でチェックが漏れていた。ここでの教訓は「ステップも場合分けもまとめられるものはまとめた方が良い。式が3つあればバグは3か所に潜むし、場合分けにより実行パスがテストケースごとに分かれてしまうと、実行されないパスのデバッグ機会が限られてしまう」。d 階層下の処理とゼロ階層下の処理を分けたのが良くなかった。自分で書いたことなので当然わかってるんだけど、コンテスト中は時間に追われるからきれいに完璧に書くことの優先度が下がってしまう。それで AC が遠のいてたら本末転倒急がば回れなんだけど。■F 問題「#(subset sum = K) with Add and Erase」。残りのわずかな時間で考えていた。最初は O(N^2) だと勘違いして愚直に DP をするものを書いた。TLE だった。そうすると DP 配列を使いまわして巻き戻しをする必要があるなと、消去法のように考えた。でもそんなんやったことないし、できるかも知らない。残り1、2分くらいで書き直したものは引き算し過ぎでマイナスの数字(modulo に近い数ということ)が出ていた。


2023年09月21日 (木) [AtCoder] 精進。日曜日に ARC165 があったが不参加だった。調べれば前回の ARC164 からふた月以上ご無沙汰で、参加しないことに後ろめたさを感じなかったのと、レート収支が常に負の ARC にレートを捧げる心構えができていなかったのが理由。■A 問題「Sum equals LCM」。答えが存在しないケースはどういうケースか。和で N を作るのは簡単で、N を LCM を維持したまま分割したときに和が N 以下にできたら、あとは +1+1+1+1+... して N が作れる。問題は LCM を維持したまま N を分割してその和を N 以下にできるかどうか。素数の掛け算で作られる数より和で作られる数の方が明らかに小さいので、とにかく分割すればいい。ただし、ある素因数 P を複数の数に分散させても LCM には寄与しないくせに項を大きくしてしまう。というわけで、N を素数の累乗ごとに分割するのが和を最小にする。それが N 以下なら答えは Yes。だけど例外があって、「2 個以上の (相異なるとは限らない) 正整数」の和で N を作らなければいけないので、素因数が1種類しかないときには1の項を付け加える必要がある。■提出 #45760781 (AC / 150 Byte / 114 ms)。Ruby によるすべての提出を見ると不自然に1秒以上かかっている提出が複数あって、これなんでなんだろうね「AtCoderの新ジャッジ、Rubyの一部提出がメチャ遅い実行時間になっているのだけど require 'prime' があると遅くなるみたいだなー」。■B 問題「Sliding Window Sort 2」。操作について気がつくこと。辞書順最大の数列が欲しいのに、範囲を昇順にソートする操作というのは得をすることがない。良くて範囲内がすでにソート済みだった場合の現状維持。N も K も制約上限が 20 万なので、範囲に対して愚直に線形時間でソート済みかどうか確かめて O(N^2) にしてはいけない。問題名に sliding とか window とか見えるからスライド最小値を使うことを考えた。つまり、ある要素がその要素から始まる K 個の区間の最小値であるとき、その区間は少なくとも操作の候補ではある。操作をしても先頭の要素が置き換えられることがないので、その1点では損をすることがないから。ところが話はもっと簡単で、ソート済みの区間が見つからないときはできるだけ後ろの方で操作をすればいいんだよね(本当に?)。■提出 #45778696 (WA×2)。ソート済みの区間は累積和で探した。サンプルの3によれば必ずしも一番後ろで操作するのが最善ではないとわかるので、直前の項を調べて操作区間をちょっとずつ前にずらすことにした。そうしたらみごとに after_contest に殺された。■提出 #45780788 (AC / 476 Byte / 164 ms)。説明が難しいな。ソート済みの範囲が見つからないとき末尾 K 個を操作の候補にする。だけど、新しく繰り入れる部分が操作による影響を受けない場合に限り範囲を少し前にずらすことができる。またそうすることで範囲から外れる部分を操作の影響から外すことができる。ずらす幅は最大で K-1 個分(※ K 個ずらして問題がないならソート済みの区間を見逃してたってことだ)。繰り入れる部分が広義単調増加(ソート済み)の場合に限る。また、最初に候補とした末尾 K 個のうちまだ範囲内に残っているものの最小値が新たに繰り入れた範囲に(操作によって)漏れ出してこない場合に限る(※この条件が抜けていたのが after_contest にやられた理由)。自分で解かないと何言ってるかわからんよ。結局この部分にスライド最小値を使うことになった。セグメント木でもいいと思う。これが B 問題で水 diff ってマジなのですか。でもまあ、A 問題で水 diff だった Reverse and Count を思い起こさせる問題ではあったかな。そっちの方がひどいね。■計算量のことを考えないなら、操作区間の先頭と、操作により最初に置き換えが起こる位置のペアを考える。置き換えが起こる位置が最も後ろのもので、区間の先頭が最も前のものが答えだと思う。結局のところ、計算量を改善するアルゴリズムを組み立てる問題だった。ケチなことを考えると色々ミスが出るのはしかたないね。


2023年09月16日 (土) [AtCoder] 今日はトヨタ自動車プログラミングコンテスト2023#5(AtCoder Beginner Contest 320)があった。コンテスト成績証自分のすべての提出。ABCDE の5完遅解き水パフォは軽傷だけど先週(20230909)に続いてなのであまりへーきではない。すべては C 問題のせい。では E までのふりかえりを。■A 問題「Leyland Number」。計算するだけ。■B 問題「Longest Palindrome」。制約上限が小さいので候補となる文字列を実際にひっくり返して比較して大丈夫。文字列比較で楽をしつつちょっとだけケチなことを考えるなら、文字列は全体を1回だけひっくり返して、あとは比較範囲を適切に変換すればいい。■C 問題「Slot Strategy 2 (Easy)」。E までで最も難しかった問題。これに 50 分費やしたのが今日の負けを決めた。答えがあるなら3周期のうちに見つかる。制約が小さいので総当たりで大丈夫。だけど判定が書けなかった。まずどの数字を揃えるかを決めて、文字列を前から見ていって、どのタイミングで揃ったのか、判断する条件が書けなかった。泣きそうになりながらネストした if 式(全部で9分岐)を書いた>提出 #45624994 (AC / 868 Byte)。他の人の提出を見るとリールを止める順番(6通り)を総当たりしていた。あ、そう、なるほどね>提出 #45651218 (AC / 172 Byte)。■D 問題「Relative Position」。BFS。■E 問題「Somen Nagashi」。1番の人から食べられるそうめんを全部食べさせていった。N 番目の人は他の N-1 人の食べ漏らししか食べるチャンスがない。残っているそうめんの管理は SortedSet を使うのが素直だけど Ruby にはないのでおなじみ BIT で代用した。終了後に Ruby の他の AC 提出を7、8個見たけどどれもプライオリティキューを使っていた。どういう方針なんだろう。時間軸に沿ってシミュレーションしているのだろうか。キューには何を入れる? あとで考える。■F 問題「Fuel Round Trip」。残り 20 分で考えていた。DP だろうなと。だけど、往路でどのガソリンスタンドで給油したかを状態に含めて区別してしまうと、それはただの総当たりになってしまう。制約を見る。ガソリンスタンドの数(N-1)とガソリンタンクの容量(H)がどちらも 300 以下に抑えられている。ガソリンの量が状態空間の軸の1つなのは間違いなさそう。ところで、ガソリンスタンドに注目したときの選択肢は、往路で給油する、復路で給油する、給油しないの3択。往路と復路の DP を同時に進めて3方向に遷移するとすると、状態がガソリンの量(H)の2乗になるけど、全体で NHH≦(300 の3乗)はありえる数字。ただし Ruby にとっては想定解が TLE になるボーダーラインがちょうどここらへんにある。■提出 #45651923 (TLE×15 / AC×22)。Cyberpunk 2077 で遊んだあと寝る前に 35 分かけて書いて、とりあえず WA は出なかった。■提出 #45653214 (TLE×5)。じっくり入念に書き直して TLE は 15 から5まで減った。なぜまだ寝ていない。5つのうち3つは超過時間が 200 ms 以下だとわかるような打ち切り時間になっている。だけどもう無理よ。■E 問題。PQ 解法。たぶん、(復帰タイミング, 復帰者) ペアのキューと、そうめん待機者のキューの2本を持つんだな。できそう。……。できた>提出 #45664340 (AC / 1273 Byte / 805 ms)。■F 問題。提出 #45663250 (AC / 1379 Byte / 94 ms)。かつて C++ は甘えと書いたものだけど、C++ なら素直に書くだけのものを Ruby であれやこれや複雑怪奇にこねくり回すのは不毛に思えてきた。そんな Ruby スクリプトは書きたくないし、シンプルでも読みやすくもないし。というわけでこれは C++ での AC。最初に TLE になった素直なバージョンを C++ に翻訳したもの。unsigned int 型が使える C++ だと、初期値としてのテキトーに大きな値と、答えがないことを表す特別な出力(-1)に共通の値が使えるんだよね。というわけで -1 を乱用してみた。■■■F 問題。提出 #45760450 (AC / 734 Byte / 1970 ms)。すでに Ruby で AC を出していた kuma_rb さんの提出 #45673011 (1885 ms) と見比べて1つ1つ修正して違いを確かめた。ローカルとジャッジサーバーとでは傾向も Fixnum のビット幅も違うのでコードテストを使って。まず DP 配列を1次元化したのが良くなかった。そのせいで添字の加工が必要になっている。高々 300 個の Array オブジェクトなど作ってしまえばいい。そして配列参照の中間結果を変数にキャッシュすればいい。2つ目はループメソッド。自分は配列参照だとか添字にオフセットを加えたりだとかの明示的な添字の操作が嫌いで Array#each_with_index や Enumerable#with_index を積極的に使うんだけど、これを Integer#upto メソッドを使った添字のループに書き換えたら 200 から 300 ms ほど早くなった。嫌だなあ。TLE を回避するためにちまちまちまちま添字の操作をしたくないなあ。だって ABC の B 問題であっても「4×4×2=32 マスの黒白を愚直に確かめるだけ。そんなんでも添字の範囲を間違えたりしてたいへん」なんだから。


2023年09月09日 (土) [AtCoder] 今日は ABC319 があった。コンテスト成績証自分のすべての提出。ABCE の4完だけど水パフォで軽傷だからへーきへーき。以下 E までの精進とふりかえり。■A 問題「Legendary Players」。えっと、冠の説明いりませんよね。知らなかったので役に立つ情報ではあったけど、余計な時間を使ったぜ。前にも書いたけど(20230729)、これはコピペ活用術が問われている。ヒアドキュメントで埋め込んで行単位でスプリットしてできた2要素配列の配列を to_h した(後知恵だけど、フラットな偶数要素の配列を Hash.[] メソッドの引数リストに展開して呼び出してもよかった)。残念なのは丁寧に整形した人よりも提出が遅かったところ。■B 問題「Measure」。読んでも理解できないので書いてある通りに実装した。ささやかな工夫は N の約数 j をループの外側で列挙したこと。■C 問題「False Hope」。正解者数が D 問題より少ないみたい。制約付きの順列数え上げなんだけど、全列挙して許されるサイズなので考える前に全列挙した。制約は多くても8個で、ある列にある数字が重複ありで AAB の2種3個のとき、A→A→B という風に同じ数2つを最初に出してリーチを演出すると高橋くんをがっかりさせてしまう。そういうケースを除外した。■D 問題「Minimum Width」。解けなかった。二分探索なのはわかる。その中で累積和を二分探索したのが良くなかった。累積和を前から順番に調べることで log を落とすことができる。これは Pairs の教訓(20230725)。しかし生かされなかった。そして実は累積和いらなかった。余計なことして無駄な時間を使って TLE を出したのみならず AC を逃した。つらい。■E 問題「Bus Stops」。Ruby で時間内に通せたのは1人だけ。それは自分ではない。クエリに対して線形時間ないしは対数時間で答えが出せないといけないがどうするか。X+Y+T.sum は必ずかかる固定の時間。停留所で生じる待機時間をどうやってすばやく数えるか。太字でこれみよがしに書かれている「ここで、1≤Pi​≤8 が制約として保証されます」がどう活用できるのか。最初は8×8の 64 通りに qi+X を分類して答えを求めておけばいいかと思った。でも足りなかった。1から8までの LCM である 840 通りであれば足りた。だけど 840×N≦8400万は Ruby にはつらい数字。D 問題に続いて E 問題まで答えは出せても TLE というのは残念すぎるので、先日の精進(20230831)にならって C++ で提出したら 624 ms で AC だった。制限時間は3秒。Ruby でも2秒を切れるみたいだけど、どんな考察が求められているのかまだわからない。■F 問題「Fighter Takahashi」。薬を飲む順番を総当たりして許される制約。敵はポーションのまわりに集約できる。ポーションは直列と並列に並べられる。あとは効率良く総当たりしたいけど、うまく書けなかった。DFS でやるんかな。■■■F 問題。すでに飲んだ薬の集合をキーとして、倒せる敵をすべて倒したときの強さの最大値を記録したものを状態とする。次に飲む薬を選び、倒せる敵をすべて倒し、記録する。これでいけると思う。DFS だと飲んだ薬の集合ではなく薬を飲んだ順番を区別してしまうので、計算量が危険? 10 の階乗はだいたい 370 万やからいけるやろって思ってたんだけど、案外遷移に時間がかかる? つまり、倒せる敵を倒しつくすのに(※薬の倍率が1以上なので薬を飲む前に倒せる敵を倒さない選択肢はありません)。プライオリティキューを使って、敵の数×log(敵の数)<5000。あっ、これはいけない。状態数を 1<<(薬の数) (1024 以下) に抑えてやっとなのだな。■ところで、Ruby ですでに AC になっている提出 #45433698 を答え合わせに使ってデバッグをしていたのだけど、こういう No ケース (4つの頂点が1列に並んでいて、1の直接の子が強さ9の敵) に Yes が返ってきて困ってしまった。答えが2択だとこういうこともあるかな。こちらは大丈夫みたい>#45434962 (require があって実行がやや面倒)。■提出 #45461169 (AC / 1755 Byte / 94 ms)。通った! こんなに不安だった提出はそうそうない。Octopus (20230902) とか? 提出をためらっているあいだに変数にコメントをつけたりなんかして。しかも2桁 ms なのが嬉しい。1.7 KB も文字を打った甲斐がある。ちなみにプライオリティキューは使ってないよ。敵は木構造を持って整列しているので線形時間のマージ操作を書いた。敵を倒すときも線形時間で走査する。これは5問目の橙 diff AC。ABC の高難度評価は当てにならないらしいけど、相対的に十分難しいのはたしか。


2023年09月04日 (月) 【悲報】妻「7万円の脱毛器買った」夫「どんな仕組みなの?」妻「ギャオオオオン!!」 → 叩きつけてフローリングと脱毛器を破壊 : 暇人\(^o^)/速報 - ライブドアブログ」「この怪談、38年生きてきた中で一番怖い。 - Togetter」■おもしろかった。ちゃんと的確なリプライがついてるし、そういう読み方をさせられるもともとの「怪談」がよく書けている。何がハラスメントかって、夫は購入に「OK を出す」立場で、7万円を「返せ」と請求できる立場で、「社会で頑張って稼ぐ男性」という立場であるということに疑いを持っていないあたり。最後にいたって全女性に喧嘩を売ってるんだからわかりやすすぎる。そういう人間がどういう仕組みで効果があるのか質問することの意味。それは実質的に審査(の続き)であるし、夫が疑っておらずしたがって妻が受け入れているらしい上下関係からくる圧で説明を回避することもままならなかったのでは。いやべつにおもしろい話ではなかったな。こうして書いていてフラストレーションがたまってきた。この「怪談」にはカタルシスが欠けている。釣り針だけで釣り宣言がない。


2023年09月02日 (土) [AtCoder] 今日は THIRD プログラミングコンテスト 2023 アルゴ(AtCoder Beginner Contest 318)があった。トップページの煌めく Kaggle Grandmaster の文字がおしゃれですね。CSS Animation で2秒のうち一瞬だけ可視不可視を切り替えてるっぽい? そういうの初めて見た。あ、任意色も任意の色をまとってアピールしてる! コンテスト成績証自分のすべての提出。ABCDE の5完は前回と同じ。同じなんだけど、前回は早解き失敗でまさかの緑パフォだったのに、今回は早解き成功で青パフォ。何この天国と地獄。あと1問解ければ解決だね(できるならね)。以下 ABCDE のふりかえりと F の精進について。■A 問題「Full Moon」。制約を見てだめな気がしたから計算で解いたけど、全探索することもできた。でもミスが怖いという点では大差ないかも。A 問題の提出にドキドキはいらないんで、もうすこし容赦してほしい。■B 問題「Overlapping sheets」。Tangency of Cuboids を覚えていますか? 私は覚えています。でもあちらは E 問題 (黄 diff) でこちらは B 問題。もうすこし容赦してください。でも今日のは灰 diff だなあ。3次元と2次元という以上の落差が謎。■C 問題「Blue Spring」。ふりかえり中に気がついたけど、アオハル=18切符ってことね。ソートして高額区間を優先して貪欲にシミュレートした。■D 問題「General Weighted Max Matching」。つい最近 ABC313-B「Who is Saikyo?」と ABC317-C「Remembering the Days」で制約が小さいからと雑に DFS を書いて TLE を出したことを覚えています。雑に書いた DFS の計算量には階乗がでてくるみたい? AC 解答も順列の全探索だったから階乗でいけないことはないと思うんだけど、Ruby では書き方によりダメということか。今日はきっちりメモ化再帰を書きました。D 問題ならそれくらいはね。■E 問題「Sandwiches」。同じ数ごとに添字を集めて昇順または降順で添字の和と添字の個数をメモしながら数えた。添字の和と個数がわかれば作れるサンドイッチの数がわかるし、添字の個数がわかれば除外すべきサンドイッチの数がわかる。■時間内に解けたのはここまで。それぞれの問題に費やした時間が A=3分、B=4分、C=7分半、D=6分半、E=10分。■F 問題「Octopus」。解答の方針は立ったけど1時間以上あっても解けなかった。このややこしい問題が時間内に解ける未来が見えない。方針はこう。X 座標の左端からスタートして右に向かって移動するタイムラインを再生する。時刻の幅が広いので局面が変化するタイミングで刻んでお宝をつかんでいる時間を数える。局面の変化っていうのは距離で並べたお宝が入れ替わること。つまりお宝の順番を固定して考えられるように時間を刻んだということ。お宝の並びが決まれば対応する腕の長さが決まり、お宝をつかんでいられるたったひとつの区間が求まる。それら N 個の区間と現在の時間の区切りの共通部分を答えに計上する。サンプルの2が親切だけど、タイムラインのスタート地点はよく考えよう。X.zip(L).map{|x,l| x-l }.min が十分。Ruby だから関係ないんだけど、制約が 61 ビット使うといっているので 10 倍 100 倍してテキトーに小さい数を初期値にすることをためらってしまった。それが WA の理由のひとつ。もうひとつが等距離にあるが右にあって近づいてくるお宝と左にあって遠ざかっていくお宝の扱い。近づいてくる方を短い触手に割り当てる方がお宝がつかめる可能性を高める。いやいやいやそんなんわかりませんて。ランダム入力を何十回試してもそれによる不一致は出てこないんだよ。まあ、無限ループなのでいずれ見つかるんだけど。■今日の日記にここまででタコの腕とタコの触手が出現しているけど、問題文ではタコの足になっている。実際のところタコのあれはなんなんだろう。「タコの足の本数は何本?タコの足の数はなぜ8本なの?腕が再生して96本! | BIJOH [ビジョー]」を読むと腕と足は間違いではないが触手には言及がない。触手について「無脊椎動物の体の前端や口の周辺にある糸状またはひも状の突起。先端に多くの感覚細胞が分布し、触覚や捕食の働きをする」と明鏡国語辞典に書いてあるのを読めば当てはまるような気がするが、違うと言われれば糸でもひもでもないからかなあとも思う。でもそんくらいしか違わないよね。ナチュラルに触手ってワードが出てくると、すわエロアニメによる刷り込みかという警戒心が働くのだけど、別に不自然な言葉選びではなかったのではないか。でもそれをここにこうして書くのはダメだと思う。


2023年09月01日 (金) [AtCoder] 精進。ABC131-F「Must Be Rectangular!」(青 diff)。最初のステップは誤読への気付き。どういうわけか角が1つ欠けた四角形があるときに「3点を消して」欠けた一角に点を打つのが操作だと勘違いしていた。そうするとグラフ構造が生じる。ある1点がどの4つの四角形に属すると考えて操作するかによってその後の操作回数が変わってくる。今日また問題を読んでみたら点を消すとかどこにも書いてなかった。次の1手は X 座標でも Y 座標でもいいからソートして順番に処理してみること。処理に方向性を与えることで考えるべき次元が下げられる。たとえば X 座標でソートして昇順に処理すると決めると、現在の Y 座標グループ(同じ X 座標を持っている)とすでに処理した Y 座標グループの関係を考えるだけで良くなる。そのようにお膳立てして考えてみて気が付いたことは、2つの Y 座標グループのあいだで操作を行うために必要なことは、2つのグループが同じ Y 座標に点を持つことだとわかった。それだけなんだ。2つの X 座標(x0,x1)と1つの Y 座標(y0)があって2つの XY 座標((x0,y0),(x1,y0))に点があるとき、(x0,y) に点があるなら (x1,y) に点が打てるし、(x1,y) に点があるなら (x0,y) に点が打てる。そうしてすべての操作が完了したとき2つの Y 座標グループは全く同じになる。2つのグループをマージしたのと同じ状態になる。ここまでわかればあとは UnionFind が火を吹いて終わり。■提出 #45108485 (AC / 668 Byte / 341 ms)。点を Y 座標で括って X 座標をグループ化する。点を X 座標で括って X 座標が属するグループに点を打っていく。あるべき点の数から実際にある点の数(全部で N 個)を引いて答え。■UnionFind を使うにしても二部グラフが見えてる人と見えてない人(私です)がいるみたい。UnionFind を使わない提出 #6097246 もあった。


2023年08月31日 (木) [AtCoder] 精進。ABC191-F「GCD or MIN」(黄 diff)。最初のとっかかりとしてまず gcd を無視して min を考えた。min だけのとき最後に残るのは A 数列の中の最小値。ここに gcd がどう関わってくるか。gcd(a,b) は必ず min(a,b) 以下の数になる。そして最小値は最後まで残すことができる。まとめると……。A 数列の最小値を M とする。M は最後まで残すことができる数のひとつであり、その中では最大のもの。gcd(A[a], A[b], A[c],...) によって作ることができる M 以下の数はすべて最後まで残すことができる。その種類数が答え。■提出 #45084618 (AC / 725 Byte / 1974 ms / 5332 KB)。DP をした。それも頭の良くない DP を。まず A[0] を配列の先頭に配置し、以降 A[1] から A[N-1] について、配列の全要素とのあいだで gcd を計算してそれら gcd と A[i] そのものを配列に追加した。重複がないとすると伸びていく配列のサイズは 0,1,3,7,15,...,2^N-1 という感じで成長していって、N≦2000 だから2の 2000 乗はやばいんだけど、実際は gcd が1になったり重複したりすることが多いのでそれなりに大丈夫。でも Ruby では4から5秒かかって大丈夫ではなかったので C++ の犯罪力に頼りました。実は C++ であっても2秒を数百 ms オーバーしてしまったので、GCC ではなく Clang を選んだり、set ではなく unordered_set を使ったり、2つ使っていた unordered_set を1つに減らしたり、初期にあまり配列を伸ばさないで済むように A 数列をシャッフルしてみたりした(※逆にソートすると悪化した)。それでも2秒を 50 ms ほどオーバーしてしまったのだけど、unordered_set に大きめの初期サイズを与えると 100 ms 弱縮んでようやく AC の目途が立った。Ruby で通してる人は素因数分解をしたりしてるんだろうか。Ruby でも3桁 ms で AC がとれるらしいんだよね。自分にはわからんけども。■ブログを2つほど読んだ。約数を列挙してそれが GCD になりうるかどうかを調べるといいらしい? さっき書いた素因数分解~云々がそうなんだけど、素因数の指数部分を減らしてみて、共通部分を持つ他の要素を(1つ以上)見つけて、非共通部分の GCD が1になるかどうかで答えの候補になるかどうかがわかる。でも計算量的にそうした方がいいという判断ができなかった。■Ruby で約数を列挙してさっき書いたようにして答えを出してみたら、many_divisors_00.txt というケースが最長で、2秒を 121 ms オーバーした。ましにはなったけどまだ考えが足りないらしい。提出 #45095866 (AC / 628 Byte / 192 ms / 9844 KB)。Ruby では TLE 間違いなしなのでこれも C++ だけど、1974 ms → 192 ms で 10 倍ましにはなってるんだな。このうえさらに Ruby で通すのつらすぎるぜ。■提出 #45096878 (AC / 259 Byte / 1762 ms)。これは Ruby。AC と TLE の分かれ目はまさかの約数列挙ループだった。while b*b<=a; if a%b==0; c = a/b end b+=1 end なら AC で、while b<=c; if a==b*c; end c = a/b+=1 end なら TLE。AC の方では足し算1、掛け算1、剰余演算1が毎回で、約数が見つかったときだけ割り算が1。TLE の方は足し算1、掛け算1、割り算1を毎回やっている。剰余と割り算のコストが同程度なら剰余を求めないで済んでる分得かなと思うんだけど、そういう結果ではない。違うんだ? ループ回数は同じだと思うし、どこで差がついてるのか本当にわからない。■3桁 ms の2つの提出(#20120433, #20414187)を読んだ。約数列挙に工夫があった。最初に素数を列挙している。たしかに1ずつ加算して割り切れるか確かめるより、素数について指数を調べる方が効率が良さそう。