/ 最近 .rdf 追記 設定 本棚

脳log[2022-11-30~]



2022年11月30日 (水) [AtCoder] 精進。ABC129-E「Sum Equals Xor」(水 diff)。数週間前には解けなかった問題。今日の思いつきはこう。ある1のビットを選んで倒す。そのとき、左にある1のビットを a,b のどちらかに割り振る。右にあるビット(0/1 を問わない)は a,b のどちらかを選んで立ててもいいし、a,b ともに0でもいい。これで重複なく数えられる。あとは1のビットを1つも倒さない場合の割り振りを忘れずに。提出 #36886000 (AC / 142 Byte / 133 ms)。


2022年11月29日 (火) リツイートされていたことで観測した1つのツイートに対する反応集。■1「何がひどいのか全く指摘していない点でひどい感想文だなという印象。論文で扱っていない因子の話がしたければレター書いたら建設的だが、しないんだろうなあ。」■2「シニア研究者の配慮を欠いた発言により、こうやってまたシニア研究者や大学教員に対する評価が下がっていくのである。」■3「論文に対して意義を述べるのは自由です。ただ、科学雑誌に掲載された論文を「ひどい論文」と表現するのは研究者として如何なものかと思いますね。 同じ地球惑星科学に関わる人間として残念です。」■4「本当に悲しいです。 陸水学を代表する先生に「ひどい論文」と評価されました。 共同研究者全員で3年間必死になって積み上げてきたことを、すべて崩れた形に。アメリカで私は何を勉強してきたのか。本当に悲しいです。」■ひとつを除いて共感できないんだよなあ。言いたいことは「悲しい」だけ? 感想ひとつで研究成果が崩れちゃうの? 論文にも論文誌にもピンからキリまであるらしいので、科学雑誌に掲載されたことをもって「ひどくないもん」と主張されてもなんだかなあ。精々確からしいのは最低限の水準と体裁をそなえた「論文である」というあたりでは? 欠けているのも求めているのも配慮なの? 思慮ではなくて? そこからあなた自身の論文への評価を読み取っても構わない? シニア研究者や大学教員に対する評価への心配は大きなお世話じゃない? 対象を個人からカテゴリに拡大しているのはあなたであって、空気の代弁者気取りはいらないよ。アカデミアを構成しているのも人間なんだなあ、というのが自分の感想。お気持ちだけで戦うの? たぶん2のツイートはゼミ生募集に際してあからさまな女性優遇を隠しもしなかった帝京大学教授に関する報道を踏まえたものだな。流れはわかったけど同列に扱うべきかどうかはわからない。自分は渦中の論文が本当にひどいものかどうか判断できないから。研究者の顔と教育者指導者の顔は別物で両方あっていいと思うから。


2022年11月26日 (土) [AtCoder] 今日はトヨタシステムズプログラミングコンテスト2022(AtCoder Beginner Contest 279)があった。コンテスト成績証。A-E 5完(1500 点)の中では最速級だったらしい。これ以上は望むべくもない結果だけど、でも、1時間以上あるなら F も通そうよ。難しい問題ではなかったと思うよ。解けなかったけどさ。終了後の提出(#36829850)も TLE だったけどさ。■精進。F 問題「BOX」(青 diff)。提出 #36832919 (AC / 443 ms)。できてると思ってた経路圧縮ができていなかったのが TLE の原因だった。普通の UnionFind にくらべてレイヤーが1枚多い感じで、いつもの Find 関数が表層の1つ下層に隠れていた。表層のショートカットだけ更新していて隠れていた Find 関数の経路圧縮ができていなかった。■宿題だった F が片付いたので気持ち良くふり返りたい。D 問題「Freefall」(茶 diff)。サンプル3について、自分の AC 提出の出力が 8772053214553.691 な一方、出力例が 8772053214538.5976562500 となっている。差は 15 とちょっと。これが「出力は、真の値との絶対誤差または相対誤差が 1e-6 以下のとき正解」という条件を満たすかどうかの判断がひとつの分かれ目だったらしい。自分は何か月か前あたりに、絶対誤差と相対誤差を正確に計るとこういう式になるのだけど……(でも必ずしも停止条件を厳密に計る必要はないよね)、的な文章を読んでいて、大きい値の相対誤差は(絶対値的には)そこそこ大きい値でも許されるんだなあ、というような、今では式もよく覚えてないけど意外性の発見があったことだけは覚えていたので、通るような予感があった。祈りながら提出して 1 WA でも返ってきたらどうしようもないなと戦々恐々としていたのも本当のところだけど。解法は g を探索する整数の二分探索(Range#bsearch)。だけど g におけるタイムと g+1 におけるタイムを比較して判定条件にしたので実質的には三分探索だった。こんなに簡単素直な解法でいいのかと疑ったけど、20210401p01.03 に書いたように ABC174-E「Logs」が自分が初めて解いた解を二分探索する問題で、二分探索に対する発想の転換があるまでは解けなかったことを思い出すと、D 問題でこの素直さはありなのかなあとも思った。早々に解析解を提出している人はすごいね。分数とかルートとか、もはや紙と鉛筆があってもなんとかなる気がしない。■E 問題「Cheating Amidakuji」(水 diff)。題意を理解するのがちょっと難しい。ざっくり言うと、A 数列の値に従って B 数列の隣接2要素のスワップを繰り返したとき、B 数列の要素1がどこへ移動するかを追跡する。A 数列の要素の1つを無視した場合(スワップを1つ飛ばした場合)の答えを A 数列の長さと同じ数だけ答えてね、という問題。スワップ対象の B_{A_k+1}B_{A_{k+1}} と紛らわしくなかった? HTML ソースを読んで判断したよ。結局は同じことをしているのかもしれないけど、人によって全然解法の背景にある考え方が違いそうな気がする。自分はといえば、まず最初に A 数列を順番に使用して B 数列のスワップを最後まで完了させた。そして B 数列の初期位置と最終位置の対応を記録した。そして B 数列を初期化してからもう一度スワップを繰り返した。その際に、A 数列のこの要素が関わるスワップを飛ばしたとしたら答えにどう影響するかを考えた。スワップする2つの要素がどちらも1ではなかったら、1の最終位置は変化しない。どちらかが1なら……。■■■@2022-12-07 F 問題を振り返ってなかった。F 問題はボールから箱を引くデータと、箱から箱ラベルを引くデータと、箱ラベルから箱を引く3つのデータを用意した。最初からこの方針だったのだけど、Find 関数の停止条件が明らかではなかったり、箱と箱ラベルを混同したりでなかなかサンプルが合わせられなかった。合ったら合ったで TLE だったしね。箱というのが普段の UnionFind で取り扱う頂点なのだけど、この問題では箱が表に出て来ない。ボールや箱ラベルから間接的に参照されている。こういうのは初めてだったし、要件に対して用意するのがこの3つのデータで間違いないという確信も持てないまま実装していた。自分としては ABC273-E「Notebook」(青 diff) を解いたときと同じ頭の部分を使っていたのであり、そのときみたいにさささっと8分で解きたかった>20221015。でもこっちの問題は骨格を決めた後の実装がちょっとややこしかったね。


2022年11月25日 (金) ■DFSを非再帰で書くとTLEになる件 再帰になっているものを非再帰に変えると普通は関数呼び出しのオーバーヘッドがなくなるので処理は速くなるはずだ。ところが、DFSを非再帰に書き換えるとTLEするようになったという心霊現象みたいな話はわりとある。添付画像のような記述だ。 つづく https://t.co/KU7V0jQfni」■非再帰で迂闊な DFS を書くと TLE になることがあるという一連のツイート。書いてある通りだと思うのだけど、結論が投げっぱなしなので補足的なことを書きたい。再帰関数で書いた DFS を非再帰版に書き換えるというのは、関数呼び出しによってスタック上に暗黙的に確保されていたローカル変数および仮引数のスタック(積み重ね)を、配列などを使い自分で明示的に保存、復元する処理を書いてループを回すということ。ループの1回がだいたい再帰関数の呼び出し1回にあたる。TLE になるのは非再帰バージョンに書き換えたときにパラメータが不足していて状態の復元が不十分だから。不十分でも WA にはならないかもしれないけど、状態と状態のあいだが疎らなために手戻りが発生して TLE になることがある(というのが一連のツイートの内容)。このツイートで再帰関数を呼び出す瞬間と戻ってくる瞬間をよく意識してほしい。隣接頂点リストを走査するループの途中で再帰に突入し、ループの途中に戻って来ている。これを非再帰で書き直すのだから、隣接頂点リストのどこまでが処理済みかという情報まで(明示的な)スタックに保存し、状態を復元しなければ等価な書き換えとは言えない。提出 #36771698 (Ruby / 419 ms / 非再帰 / 頂点番号と隣接頂点リストの添字を記録してループ)。提出 #36771423 (Ruby / 409 ms / 非再帰 / 頂点番号だけを記録しているが隣接頂点リストを破壊的に使用して手戻りを防いでいる)。ちなみに迂闊な DFS をあえて再帰で書くとこうなる>提出 #36774583 (TLE)。隣接頂点リストを繰り返し走査する 14 行目を見れば O(N^2) が納得できると思う。再帰↔非再帰を行き来するときに知らずこのような違いが生じているので TLE でなかったものが TLE になったりする。それゆえに「結論 : DFSを非再帰で書くのはわりと難しい」。でも心霊現象ではないと思うの。■再帰→非再帰の書き換えを初めて意識したのはこのとき>脳log[20050929p01] やねう企画2005年度入社試験。見覚えのあるお名前。■■■@2022-12-21 八百倍役に立つ記事があるよ。「それ、非再帰で書けます - Qiita


2022年11月20日 (日) [AtCoder] 精進1。昨日あった ABC278-D「All Assign Point Add」(茶 diff)。クエリ1を愚直にやってはいけないのはわかる。だから Hash を使った。提出 #36620785 (TLE×2)。ちなみにこちらの提出 #36623538 が素直に配列を埋める解法で、全く同じケースに対して TLE×2。どうして2つが同じ性質を示すのかまるでわからない。昨日はもう、TLE になるはずがないと考えてしまって現実に対処するのをやめてしまった。今日の提出1#36664071 (329 ms)。配列2本を使った解法。1つにはクエリ2の累積和を、もう1つには累積和のベースになっているクエリ1のバージョンを記録した。古いバージョンに基づく累積和は無視する。今日の提出2#36664254 (351 ms)。昨日の Hash を使った解法を踏襲したもの。この提出 #36664376 (TLE×2) と比べて間違い探しをしてみよう。Hash#clear を呼ぶと TLE だが代わりに = {} で Hash を使い捨てにすると AC。ベンチマークをとってみても差が出ないので単純に Hash#clear が遅いという話ではない。謎。こういうツイートを見かけた。「AtCoderのABC278D、TLEするのでこんなところで解けないとは耄碌したなーと思ったが… unordered_mapで書いてたところを普通のmapに書き換えたら通った。普通逆じゃないかと思うんだけどまだまだC++、奥が深い。」 Ruby の Hash 実装に固有の秘孔ではないらしい。■精進2。同じ ABC278-F「Shiritori」(水 diff)。D の次に解けたのが F なので E を飛ばして F。bitDP が見えたらちょっと下手なことをしても通る制約。ただし 16 要素の順列はちょっとではない。やるだけ。でもできなかった。提出 #36668555 (AC / 66 ms)。Hash のデフォルトプロックを使ったメモ化再帰で書いた。DP はボトムアップの解法なんだけどメモ化再帰で書くとトップダウンになる。トップダウンの方が頭の中を素直に反映していて書きやすい。気をつけるべきは、DP の詳細を詰めず状態をまとめないままメモ化再帰を書いてもメモ化の意味がないし、TLE になるということ。■精進3。同じ ABC278-E「Grid Filling」(緑 diff)。これが緑 diff ってマジ? 俺は制約を見て、h=H/2; w=W/2 のときの計算量を計算して、諦めたよ。D と E のコンボでいじけちゃったよ。提出 #36685628 (AC / 737 ms)。書いてみれば通った。2次元累積和はたぶんかなり苦手にしている。それを尺取りにしようなんてしたら脳みそがオーバーフローしてもしかたがない。実態は「書いてみれば」の語の印象の通りではない。138 ms と飛び抜けて速い提出がある>#36636856。数の種類ごとにそれを覆う最小の矩形を求めておいて、それが黒マスに含まれるかどうかを効率良く(変化量の累積和の累積和で)調べてるっぽい。累積和を使わないとこちらのように短く書けてそれでも通るっぽい>#36649717 (347 Byte / 1586 ms)。まねまね>提出 #36698536 (AC / 560 Byte / 131 ms)。答えを求める部分(末尾5行)をこういう風に書きたいというところと Array#transpose を失敗させない条件から逆算して配列の要素数と添字を調整している。ABC278_e.rb27←これを最終形にしたいけど大勢に影響しない程度の計算量の削減(※黒塗りがグリッドの上(左)にはみ出しているときの変化量を1行(1列)に圧縮した)と見た目の違いしかないので提出を控えている。■今日の ARC152 はどうだったか。自分のすべての提出。1時間半かけて2完のノルマは達成した。B の提出が完成してから 15 分くらい粗探しに時間をかけていた。粗というのは書き損じではなく考え違い。ARC では慎重になる。自分にとって2時間で3問を解くコンテストなので ABC とは違う時間の使い方ができる。昨日の ABC がひどかっただけに慰められる結果。C 問題は B 問題の後だったので図形的な意味はすぐに掴めたのだけど、そこからがさっぱり。解説放送があるよ>「AtCoder Regular Contest 152 - YouTube」。


2022年11月14日 (月) [Ruby] 「Ruby 3.2.0 Preview 3 リリース」を読んでいたら「Regexpのマッチングアルゴリズムの改善」という項目があった。メモ化によってマッチ時間が改善するという。「Feature #19104 未完了 Introduce the cache-based optimization for Regexp matching」 位置と状態をキャッシュするっていうから 20220218 にテキトー書いていたことがあながちテキトーではなかったってことかな。Limitation の項目に書いてあることが面白い。まあそうでしょうという感じ。緩和策なのでそういう場合は無効にするだけ? ReDoS 一般が話題になった時期があったけど、タイムアウトの採用もあり、見つけた問題を着実に解消しているのだな。


2022年11月13日 (日) 『ロマサガ』の自由度と難度が高い理由。『ミンサガ リマスター』河津秋敏氏動画インタビューの内容を全編書き起こし | ゲーム・エンタメ最新情報のファミ通.com」■先日整理していた本の中に『Sa・Ga 2 秘宝伝説 完全クリア編』があり、94 ページの開発者インタビューに添えられていたプロフィールによると河津氏は 1920 年生まれなので 8 日前に 102 歳になっている(そんなはずがない)。ヤクーツク出身ってなんだ。


2022年11月12日 (土) [AtCoder] 今日は大和証券プログラミングコンテスト2022 Autumn (AtCoder Beginner Contest 277) があった。コンテスト成績証自分のすべての提出。ここひと月ほど ARC の開催が途絶えていて、そうするとレートが安定するんだなあ。今回は E 問題でも緑 diff 止まりだった一方、F、G 問題がともに橙 diff だったらしい。F 問題はてっきり自分が苦手なタイプの水 diff だと思っていたので意外。■G 問題「Random Walk to Millionaire」の方が見込みがあると思って考えている。とりあえずサンプルが合うところまではできたのだけど、これは O(KKM) であり K,M≦3000 の制約下では時間オーバー。報酬がレベルの2乗だというところが嫌らしいと思うんだよね。2乗でなければレベルの和と個数を覚えておくだけで良さそうなところが2乗だからまとめることができないでいる。レベルごとに独立して遷移を行うせいで K(移動回数)×K(レベル数)×M(辺の数) の計算が必要になってる。■F 問題「Sorting a Matrix」も考えてる。行のソートと列のソートをそれぞれでがんばれば解ける気がするんだけど、「がんばる」が曲者。行のソートは比較的問題が少なくて、列の統計情報を一度記録しておけば素直に比較ができる。列のソート。行ごとに非ゼロの列を選んでソートして列の序列を作り、マージして1本のソート列にする。入力がすべて非ゼロだった場合、最初のソートに O(HWlogW) かかるのと、扱う2項関係が 行×(行内の2項関係) = HW(W+1)/2 個存在するのがつらい。最大ケースで数十秒かかる。あと制約も曲者で、H と W のそれぞれは上限を持っていない。ものすごく縦長の行列もものすごく横長の行列も考えられる。ソート部分の logW がほぼ logHW だったとしても問題はないけど、2項関係の W(W+1) は非常によろしくない。ソート列をソート列として保持したままうまくマージする方法は。それ以前の問題として、エラーなしでは最初の提出 #36477837 (WA×12 / TLE×5) が一番 WA と TLE の数が少ないこともある。愚直にやったものの TLE が一番少ないところと、愚直なのに WA があるところが問題。ケアレスミスなのか根本の方針が答えにたどり着けないものなのか。ダメケースを1つ2つ見つけては結果が改善してるのを確認してるのに提出すると WA 数が何も変わらないのつらい。列のソート(比較)の肝は一方または両方が 0 の行では順序づけを保留することと、同値の場合はグループ化することだというのが今の理解。■@2022-11-14 G 問題。ヒントを見ました。「G は i 回目の移動で頂点 u にレベル X で来る確率が p だとすると、知りたいのはすべての i と C_u=1 なる u について pX^2 の和。(i,u) を状態として \sum pX^2 を管理しようとすると、レベルアップの処理が \sum pX^2\rightarrow\sum p(X+1)^2=\sum pX^2+2\sum pX+\sum p となる。よって \sum p\sum pX\sum pX^2 を管理しておけば更新も含めて計算できる。」 これがヒントであって答えでないのは、これを読んでさえさっぱり遷移が書けなかったから。何度も何度もガチャガチャやって考えてガチャガチャやって考えてお風呂に入って考えた。提出 #36502350 (TLE×13)。書けなかったのは 29 行目と 30 行目と 32 行目。それで苦労の結果が TLE。計算してみたら N,M,K≦3000 で Ds.size=3 で E.sum(&:size)=2×M で C0.size+C1.size=N だからループの回数が K×(3×2×M+N) = 6300 万。Ruby は 1000 万回を超えたあたりから厳しくなってくるのでこのままでは無理だよ。


2022年11月10日 (木) 体験版をプレイしただけだと思っていたオーディンスフィアの製品版(PS2)を持っていたことがわかったので 15 年ぶりに再度始めて今度こそクリアを目指している。しかしやっぱりアクションが厳しいなあ。慣性があるでしょ、自動で滑空して空中の的になるでしょ、攻撃への対抗手段が距離を取るか防御か飛び越えるかでしょ、敵のモーションを見てからじゃ対処が間に合わないんよ。事前に距離を取っていても巨体の頭に引っかかってはじき飛ばされるし、いっぱい湧いてくる特定の敵に触れると凍結して動かない的だし、アイテムは現地調達が基本で備えができないし、被弾しても時間をかけてもスコアが下がって報酬が減るし。時間を制限するなら全体を概観する手がかりを与えてくれないとペース配分も何もなくノーダメージを狙うのもままならなくてただただ忙しいだけ。クリアしたくなるハードルとしてのゲームの枠組みの作り方が下手だと思う。声と絵の良さでまあまあ楽しくやってるけどレイヴスラシルではいっぱい改善されてるんだろうなあ。なんでオーディンスフィアかっていうと「十三機兵防衛圏クリアした!」から。


2022年11月09日 (水) 精進。東京工業大学プログラミングコンテスト2022 の先頭から5問。難易度順に並んでるらしいですよ。コンテストトップ画面がすごく懐かしくもうざい感じで、画像ではなかったので思わずソースを覗いてしまった。IE が咲かせた HTML の徒花マーキータグさんじゃないですか(お仲間にブリンクタグがあるがこれは MSIE の仕業ではない)。クリックすると方向が切り替わる無駄ギミックもサイコー。サイコーにうざい。■A 問題「Next TTPC 2」。差を取って GCD の約数。提出 #36331477 (AC / 209 Byte / 100 ms)。■B 問題「Magical Wallet」。一番単純な形の DP。所持ディジットをキーにして購入数を記録する。遷移にあまり時間をかけないようにする。提出 #36331824 (AC / 382 Byte / 1243 ms)。Ruby で 326 ms の提出(#36329646)があるのでまだまだ。■C 問題「Five Med Sum」。慣れないと5重のシグマ記号は意味不明だけど、これは5つの数列から作れるあらゆる組み合わせの5つ組を考えて足すという意味。「あらゆる」と「足す」だけ認識していれば十分。ある数列のある要素に注目したとき、他2本の数列に小さい要素がいくつあるか、残り2本の数列に大きい要素がいくつあるかがわかれば、現在の要素が中央値として選ばれる場合の数がわかる。提出 #36332261 (AC / 504 Byte / 1918 ms)。制限時間ぎりぎり。同じ値を持つ要素の扱いに困ってしまったけど、実は気にする必要がない。むしろ気にせず便宜上の大小関係があるものとしたほうが重複して数えてしまわずに済む。提出 #36339767 (AC / 513 Byte / 608 ms)。かなり速くなった。このときの実践「ループ展開した文字列を eval するもの。そんなテクニック初めて見たよ! 横目でちらちら見ながらまねしてみたけど、なんでか同等の速度は出なかった。繊細なのね。」 この提出 #36350174 の 18 行目の式が賢い。自分の提出の sumf 変数をこの式にしたい。最初の提出の時点で考えなかったことはないけど、全然まとまらないねってそのときは思った。HI/LO を入れ替えた2つの和を左からと右からでまとめてるっぽい?■D 問題「XOR Tree Path」。最初はビット列を掛け合わせて寄せていく解法(名前がわからん)かと思ったけどだんだん木 DP が見えてきた。葉ノードから寄せて上げていく。記録するのは、反転操作を親に伝播させるときに部分木がいくつの黒ノードを含むかと、そうでないときにいくつの黒ノードを含むか。木 DP の肝は子ノードのマージ部分だけど、ここに罠があった。子ノードが1つしかないときに「反転操作を親に伝播させるときに部分木がいくつの黒ノードを含むか」という数字を反転しない場合の数字に反映させる方法はない。反転操作を打ち消し合う他の子ノードが存在しないから。提出 #36339752 (AC / 602 Byte / 286 ms)。■E 問題「Name Value」。制約が、特に A,B 文字列の長さがやばすぎるだろうと思ったけど、クエリ文字列のスキャンが前方/後方から貪欲に行えるので、それを効率的にやるために準備を頑張ればいい。文字種×(A,B 文字列の長さ) = 2000 万要素くらいの遷移表を用意して許されるのか考えたよね。うーん、ありかなあ。でも1要素ずつスクリプトでコピーするとダメかも。提出 #36341147 (AC / 509 Byte / 1309 ms)。許された。最初は遷移表に添字を記録していたけど、遷移先の遷移表を持たせるのがより直接的ではないかと気がついてそのようにした。人間が脳みそに余計なステップを持っているとコンピュータにも無駄な回り道をさせがち。こういう無駄を詰める思考はコードゴルフと通じている。提出 #36348178 (AC / 392 Byte / 1216 ms)。Array#dup メソッドの呼び出しを省いた(20201207)のと遷移表のサイズを半分にしたのと無駄な Array#min を省いたのと添字を記録していたなごりの無駄な変数を省いた。遷移表のサイズを半減した引き替えに整数の引き算がループの中に4回出現している。メモリ大量確保&コピーとちょっとした整数演算ではどちらが時間を食うかという判断。平均的に速くなったけど重いケースではそれほど変わらず。1度に1要素しか更新しない遷移表を毎回丸々コピーするのがもったいないんだよなあ。空間がではなく時間が。でもたぶんそれをやめると探索が必要になって log が付くのでは? インチキみたいなうまい方法があればなあ。遷移表の行と列を入れ替えるみたいな方法はたぶんうまくないんだよなあ。2要素の遷移表……。■F 問題からは解けません。■G 問題「Count Arithmetic Progression」を考えてる。傾きに注目するにしても直線の切片に注目するにしても 10 の 12 乗以下という制約がネックになって部分点しか得られない(答えが違えば部分点ももらえないが)。注目するなら 30 万以下の要素しかない L,R 整数列になるのかなあ。それで解く方法が見えないけども。@2022-11-11 部分点をもらいました>提出 #36390042。部分点の制約でも青 diff は下らないと思うなあ。探索できる要素ってある? 1ずつ計算せずに済むような単純な比例関係があったりする? どっちも(見つから)ないんだよなあ。DL,DU 変数を賢いデータ構造で仮想化して妥当な計算量で求められるとしたらどう? DU,DL 変数は傾きの上限下限だから傾きを制約する数字が連続的に変化していったら結局 10 の 12 乗に比例したステップが生じると思う。まとめてひと束で計算できる状態の単位が見えない。数列の要素数に比例したステップしか生じなかったりする? じゃあ傾き制約の変化点を順に知る方法は。■@2022-11-16 解説を読んだ。Convex Hull Trick の名前を知った。「Convex Hull Trickを知っていますか?僕は知っています。 - Qiita」「傾きの単調性が要らない Convex Hull Trick - kazuma8128’s blog」 読んだ。直線をソートして順番に交点を求めて上限/下限の変化が知れるらしい。蟻本で既出だったことも知った(初版 286 ページ K-Anonymous Sequence)。しかし書けない。


2022年11月07日 (月) [AtCoder] 精進。先週末の ABC276-G「Count Sequences」(黄 diff)。当日は愚直解さえ作れなかった。今日また考えてみたところ当初の方針だった DP/メモ化再帰とは違って組み合わせで解けそうだった。こう考える。0 から M の値の範囲に N 項の数列がある。両方の制約から各項は前項から最低 1 は増加していなければいけないので、+1 操作を N-1 個の項間に挿入する。値域が M 以下に制限されているので追加で可能な +1 操作は M-N+1 回。これを上限として追加で何も操作しなくても構わない。追加で考えられる操作は、N-1 個の項間それぞれに最大で1回だけ +1 操作を挿入することと、+3 操作を N-1 か所のどこにでも何回でも挿入すること(もちろんプラスの合計が M を超えない範囲で)。+1 操作の選び方と +3 操作の選び方はコンビネーション一発の定数時間で求まる。そうして値域としてある幅を持った数列の種類数が求まる。それが M-5 の幅だった場合、たとえば 0=a1<...<an=M-55=a1<...<an=M など、初項 a1 が取り得る値が 0 から 5 の 6 通り考えられるのでこれを掛ける。困ったのは、+3 操作の上限を決めて 0 個、1 個、2 個と挿入する場合の数の合計も、+1 操作の上限を決めて 0 個、1 個、2 個と挿入する場合の数の合計も事前に累積和を計算しておくことで定数時間で求められるのだけど、初項が取り得る値のバリエーションを累積和に掛ける方法がわからなかった。たとえばメインループで +3 操作の回数を決め打ったとする。追加で可能な +1 操作回数の上限がわかるので累積和を引く。しかし +1 操作回数が 0 回のときに初項が取り得る値の種類数と 1 回のときに取り得る種類数は異なる。+1 操作の選び方に階段状の倍率を掛けたものの累積和が欲しい。倍率はスライド式であり、×5,×4,×3 かもしれないし、×3,×2,×1 かもしれない。あるいは初項の前へのプラス操作が他と統一的に数えられたら。困ったときはお風呂で考える。普通の平坦な累積和と階段状の累積和を組み合わせればいける。いけなかったのは、N,M の制約上限が普段より厳しい 10 メガなのであり、コンビネーションの事前計算に加え累積和を2本も作成する 3N の処理で制限時間の4秒を超えてしまったこと。10 秒まで実行されるコードテストで 4.4 秒からなかなか縮まなかった。■提出 #36313584 (AC / 773 Byte / 3938 ms)。C1 メソッドの値から A 数列を作成するときに呼び出し回数を半分に節約したり、メソッドの中身をインライン展開したり、メインループの共通項を ICI として括り出したり、fn 数列の前部を切り捨てて添字のオフセット計算を省略したり、いじましい努力の結晶ですよ。Ruby によるすべての提出を見ると、tinsep19 さんの提出は最悪ケースこそほぼ同じタイムだけど、内訳を見ると1秒早かったり倍早かったりするものが多い。4秒ぎりぎりの最悪ケースの位置が異なってるのが面白い。向こうの最悪ケース(1つだけ)はこちらの最悪ケース(13 個)ではないのだ。■メインループを逆順にすると必要なのが累積和の末項だけになって事前の作成が不要になる気がするなあ。■提出 #36318378 (AC / 736 Byte / 3061 ms)。メインループを逆順にして累積和が配列2本から変数2個になった。*smallN* ケースだけやけに遅くて最適化の余地があるみたいだけど、それ以外は概ね良好かな。汚いという意味では良くないけど。■「スナネコ「ABC276Gの解説に追記しました」 https://t.co/9pw10g1SOS https://t.co/bRGlSd8RcW」。異次元からの眺め。さっぱりすぎる。


2022年11月05日 (土) [AtCoder] 今日は ABC276 があった。コンテスト成績証自分のすべての提出。AC までの所要時間は A=2分、B=3分、C=16分、D=5分、E=16分、F=32分。青パフォで Highest 更新でいい日だなーと思ってたんだけど、E、F がそれぞれ緑 diff、水 diff だったと知って AtCoder の参加者に畏れをなしている。結局あれだ、簡単に解ける問題を(やや)早く解いただけ。実際考えるところは F 問題までなかったし、F 問題も実装しながら考えを固めていった感じ。■A 問題「Rightmost」。サンプルに助けられた。String#index で書いていたところを String#rindex に修正。■B 問題「Adjacency List」。教育的問題。グラフ問題で辺の集合を受け取って頂点本位の隣接頂点リストを作成する練習。■C 問題「Previous Permutation」。100 要素の順列を全探索はできない。入力から(じか)に直前の順列を作成したい。サンプルを眺めるだけでも手の付け所はすぐにわかると思う。辞書順で後ろにあるものは必ずどこかで隣接2要素の大小関係が初期状態から逆転して降順になっている。それを1段階解消したものが直前の順列。最も後ろの逆転が最も辞書順に与える影響が小さいので解消すべきはそれ。あとはサンプルを見ながら実装をがんばる。けっこう面倒くさかった。これは今の思いつきだけど、普通の数って組み合わせの数じゃない、それを順列の数に変換する手順があれば、K-1 にその手順を適用するだけで答えになりそうだけど、そういう手順はないのかな。10 進数を 2 進数に変換するみたいな手順。……。できそうだけど K がべらぼうな大きさになるからうまみがない。■D 問題「Divide by 2 or 3」。できる操作は素因数から2と3を取り除くだけ。とりあえず全部の2と3を取り除いてから全体が一致するかを比較したり、不必要に取り除きすぎた2と3の分の操作回数を補正したりするのもありだけど、全体の GCD を予め求めて A 数列から取り除いておくのがやりやすいかな。■E 問題「Round Trip」。グリッド問題は制約を一番に見る。一瞬、行と列のそれぞれが1メガだからすべてのマス目をなぞることができないのかと思ったけど、行と列を掛けたマス目の合計が1メガに収まるという制約だった。どうやって環状のパスを見つけるか。スタート地点の上下左右をスタート地点にして(ややこしい!) BFS で陣取りをしていって、異なる陣地がぶつかったときに Yes かなと思った。スタート地点の上下左右からどれかをスタート地点にその他をゴールにして BFS なり DFS をするのもありかな、とは終了後に考えた(※元のスタート地点を経由する長さ2のパスを抜かりなく除外すること)。グリッド問題をグラフに見立てて辺を陽に持つのは原っぱのようなグリッドのときに厳しいかな。大は小を兼ねるというけどサブクラスに固有の最適化を手放す余裕が Ruby にあるかどうか。■F 問題「Double Chance」。制約が 20 万なので累積的に求めていかなければ時間が足りない。カードが1枚ずつ増えていくときにそのカードの寄与を考える。最初は答えそのもの(期待値)を更新していこうとしたが、これはうまくなかった。K 枚での期待値と K+1 枚での期待値では分母が異なるので、直前の分母を掛けて今回の分母で割って、など面倒くさい。すべてのパターンの max(x,y) を合計したものを累積的に更新するのが良い。K=1 なら 1 通りを合計したもの、K=2 なら 4 通りを合計したものを記憶する。K が k から k+1 に増えるときは (k+1)*(k+1)-k*k = 2*k+1 通りの max(x,y) を加算する。更新しながら利用できる累積和が必要なので BIT の出番。BIT は(配列を使った累積和と比較すると)取得が対数時間になってしまうけど更新も対数時間で済むのが嬉しい。■G 問題「Count Sequences」(黄 diff)。とっつきは悪くないと思ったんだけど、愚直解も作れないのでは論外。


2022年11月04日 (金) AtCoder Type Checker「ds14050 さんのスコアは 0.06 です。中間的なタイプです。/ds14050 さんの内部レート: 1438.00/計算に使用したコンテスト数: 182/ds14050 さんの平均順位率: 0.4461/内部レートによる補正値: 0.4455 (ds14050 さんと同程度の内部レートの人が取得している、平均的な平均順位率)」■得点が同じ人同士で比べたときに 100 人中 44 位くらいの位置(早さ)にいるらしい。その位置というのが自分と同レートの人の平均とほぼ同じなので、「中間的なタイプです」。Ruby を使っていてソースが短い(タイプ数が少ない)ことを勘案すると、考えるのもタイプするのもかなり遅い部類なのでは? 言語ブーストがあってやっと中間。再度「俺の脳みそのクロック周波数は常人の3分の1だ!