/ 最近 .rdf 追記 設定 本棚

脳log[2023-12-23~]



2023年12月23日 (土) [AtCoder] 今日はユニークビジョンプログラミングコンテスト2023 クリスマス (AtCoder Beginner Contest 334) があった。自分のすべての提出。配点からわかる通り、B 問題と C 問題にちょっと癖があった。それぞれ 10 分と 17 分かけた。そのかわりではないだろうけど、D 問題と E 問題は手を動かすだけの問題だった。そして F 問題。40 分残していて、急がば回れということで(「Rational でサンプル1を試したらね、1/3 になるべきところが 4/3 になってることがわかってね、(略) mod の数字を見てもデバッグはできない。」)、まずは愚直解を作るところから始めた。そして TLE が出た時点で残り 10 分弱。12-14 行目がまずいのはわかっていて、どうすれば改善できるかもわかる。しかしデバッグが完了したときには終了後 14 分経っていた。あとで書くけども、ある行を2行上に移動するだけのことだった。くやしい。ではふりかえり。■A 問題「Christmas Present」。if 文が書けますかという問題。■B 問題「Christmas Trees」。負の数がいやらしいですね。負の数の整数除算の丸め方向を気にかけたくはない。M の倍数分 L と R をずらしても答えは変わらないので、L と R が A より右に来るように加算した。あとは L-A ≦ i*M となる最小の i と、j*M ≦ R-A となる最大の j を求めて j-i+1 が答え。整数除算を使った切り上げ切り捨て計算なんておなじみなのに、i と j を求めるのにもどうやって負の数を出さずに済ませられるか数分考え込んでしまった。頭がはたらいていない。■C 問題「Socks 2」。難しいよね。制約を見ると、ペアがそろってなくなることはないみたい。片足だけもしくは両足そろった靴下が隙間なく連続して並んでいる。片足-両足-両足-片足 という風に並んでいるとき、左から順番にちぐはぐなペアを作っていっても、真ん中の両足ペアを省いて両端の片足をペアにしても奇妙さの合計は変わらないみたい。じゃあ半端ものだけでペアを作ることを考えて良い(たぶん両足ともなくしてしまったことによるギャップがあるとそうはいかなかった)。そして総数が偶数のときは左から順番にペアを作っていって良い。では奇数のときはどの靴下を省くのが最良か。これって C 問題でしょ、絶対簡単な考え方があるよなという疑念に包まれたまま、左右からの累積和を使って総当たりで最良の答えを探した。■D 問題「Reindeer and Sleigh」。トナカイとスライもしくはスレイ(※読みも意味も知らない)。トナカイは匹ではなく頭で数えたい気がしました。ソートして累積和を用意して二分探索をする。■E 問題「Christmas Color Grid 1」。問題文がちょっと難しいけど、隣接する # のマスでグループを作って、. のマスがいくつの異なるグループを連結してその結果連結成分がいくつになるかを数える。実装していて気がついたけど、. のマスが新たな孤立グループになることもある。期待値を求める式に難しいところはない。すべての . マスについて、連結成分の数を数えて合計し、. マスの数で割る。■F 問題「Christmas Present 2」。DP をしたいけど線形時間で解けと制約がいっている。どうしましょう。いったん時間を忘れると、ある子供のところに行くにあたり、2通りのルートが考えられる。(プレゼントが1つ以上あるなら)直前の位置から直行して手持ちのプレゼントを1つ減らす。もしくは、サンタさんの家に寄って K 個のプレゼントを手にしてから来て、K-1 個にプレゼントを減らす。K+1 要素のデックに移動距離を記録して、shift、push、最小値の取得をすれば答えは出る。しかし TLE が避けられない>提出 #48793891 (TLE)。アイデアはある。K+1 要素の固定長ではなく、スライド最小値の要領で意味のある値だけを記録すればいい。全要素に対して移動距離を加算したり、プレゼント数を減算したりしたくなるのは、ベースとなる値をそれぞれに用意して、それとの差分を記録するようにすればいい(ベースを増減すれば全体が増減する)。提出 #48800858 (AC / 415 Byte / 305 ms)。数字が合わなくて提出が間に合わなかったのだけど、原因は 12 行目の処理を 13-14 行目の後ろに置いていたこと。基準値(d0)を加味した値と基準値からの差分とを比較してはいけない。あーあ、解けてたのになー。■それでも青パフォうれしい。コンテスト成績証。最近不調だったけど、今日のここが定位置だと自認している。次のステップは F 問題を時間内に通すことなんだよな。■F 問題を Ruby で最初に通した人はセグメント木を使っている。最小値を取得するのにセグメント木を使いたくなったのは自分も同じだけど、shift/push をどうするかがわからなくて深追いしなかった。セグメント木で解けるとわかってそういう前提でもう一度考えてみると、N+K 個くらいの要素数でセグメント木を初期化しておいて、参照する K 幅の範囲を1ずつ右にずらしていくという方法で shift/push 操作がまかなえるのではないか。セグメント木をそういう風に使ったことがないので思いつかなかった。……と思ったんだけど、セグメント木を使う2つの提出 #48799502#48806332 を見てみるとそれぞれ K+2 と N のサイズで初期化している。なんで全部違うんだ。いや、まあ、説明できないことはない。N+K と N は、最初は参照するべき値が K 個ないことを考えると実質同じようなものだし、K 要素の方は、無効になったインデックスに新しい値を詰めるようなリングバッファ的な使い方で理解できる。実際にそういう使い方をしているのかは確かめていないので知らないけども。


2023年12月19日 (火)

最終更新: 2024-01-18T16:02+0900

[AtCoder] JOI 2023/2024 二次予選 過去問

AtCoder の問題ではないが精進。ABCE の4問。

 A 問題 カードゲーム 2 (Card Game 2)

基本的にはカードを順番にスキャンして判断をする。ハッシュ表を使って自分が一番小さいカードである場合、2番目、3番目の場合の3パターンについてカードが3枚揃っているかを確かめる。もしくは、昇順にソートして自分が一番大きいカードであるケースについてカードが揃っているかを確かめる。

 提出 #48433809 (AC / 121 Byte / 125 ms)

 B 問題 買い物 2 (Shopping 2)

N 個の商品がありそれぞれの商品は M 種類の商品カテゴリのどれかに属している。M 日間のセールがあり、セール期間のある一日にはあるひとつのカテゴリの商品が半額になる。Q 人の客がそれぞれある日に訪れ、ある範囲の連続する商品を購入する。さていくら? Q 個答えよ。

割引がなければ範囲の総和を知るのに累積和を引くだけ。しかしある日にはあるカテゴリの商品が半額になっているので、範囲内にそのカテゴリの商品がいくつあるか知りたい。カテゴリごとに位置を昇順に記録して二分探索をした。

カテゴリごとに累積和を用意しようとすると最悪の場合長さ N の配列が M 個になって、N×M は大きすぎる。更新のあったところだけ記録しようとすると、さっき書いた「カテゴリごとに位置を昇順に記録」する方法になる。

 提出 #48434188 (AC / 397 Byte / 782 ms)

 C 問題 白色光 2 (White Light 2)

難しいね。いっぱい間違えた。制限時間が1秒とやや厳しめ。

3の倍数の長さ(0,3,6,9,...)の範囲を切り取ってみれば、左端の位置と右端の位置、それと RGBRGB...RGB 文字列との不一致の数からコストがわかる。不一致を単位時間で数えるためには3種類の累積和を用意しておけばいい。すなわち、RGB...RGB..BRG...BRG..GBR...GBR.. 文字列と S との不一致を数えた3種類。

ところで、N の上限が 20 万のときに範囲の両端を自由に選ぶと 20 万の2乗(割る2くらい)で間に合いませんね。範囲の左端を総当たりすることにして最善の右端をどうやって決めるかというところで3回 WA を出した。

最初の提出 #48448208 では左端を右に移動するごとに右端を左右に移動させてみる尺取りをした。特定の制約を追加したサブタスクはクリアしたが、N の制約を甘くしただけのサブタスクで一定割合の WA がある。時間は間に合っていて(局地的に)最適な答えを見つけることもできているが、最善の答えをときどき見逃しているということ。

次の提出 #48453131 は不等号にイコールを追加するような微修正で、3割くらい WA の数は減ったけど本質的な誤りは修正されていない。

3番目の提出 #48453143 は3つのサブタスクで2つずつ WA があるという結果で、ほぼ AC と同じ。実はこれまでの2つの提出ではケアしていた、左右どちらかから全ての文字列を削除するケースに対応したコードを削った結果 WA を生んでしまっている。正しい解法を見つけたときにもういらない気がしたんだよなあ。

 提出 #48453322 (AC / 481 Byte / 224 ms)

これが AC。右端を見つけるのにスライド最小値(?)だと思うものを使った。実装はしなかったけど三分探索も検討していた。知っている解法を総当たりして正当性の判断をジャッジに委ねるのはやめようね。

 E 問題 高速道路の通行料金 (Highway Tolls)

時間に依存したコストをどう扱うか。t=0 となる位置を1から N へのパスの中のどこに置くかでコストが変わる。パスの外に置いても得をしないので考えない。どこに置くのが最善で、どうすれば最善が見つけられるか。

t=0 となる地点をあるパスの中のある辺に置くとする。1がある方のパスに A 個の頂点が、N がある方のパスに B 個の頂点があるとして、A<B なら t=0 の地点を辺上で N の方に近づけるのが良い。A=B なら辺上のどこにあっても同じ。なので、t=0 の地点をどこかの頂点から選んで良い。

t=0 となる頂点を決め打って、1からの最小コストと、N までの最小コストが求まれば良い。1からは順方向に探索し、N からは逆方向に探索すれば、2回の手間で答えが求まる。N 頂点全てを始点にして N 回の探索をすることは時間的に許されない。

 提出 #48650001 (AC / 635 Byte / 113 ms)

提出日の開きを見るとわかるけど、この AC はほぼ一週間がかりだった。

探索は BFS。移動コストが固定値の C と、パスに含まれる頂点数に比例する L×K なので、BFS によって頂点数が単調増加だと想定できると、単純にコストの総和を比較するだけで最短経路が見つけられるし、頂点数に比例したコスト計算もやりやすい。

サンプルの4、5、6あたりの答えがいつまでも合わなくて、ああでもないこうでもないと試行錯誤していた。さっぱり見えていなかったのは AC 提出の 22 行目、頂点 N までのコストを探索する D 関数に与える初期値(DN = D[ヨ[N].group_by{|s,|s}.map{|s,stcs| [s,stcs.map{_3}.min] }.to_h,ヨ])。1からのコストを求める 21 行目(D1 = D[{1=>0},E])と比較すると長さだけ見ても段違いなのがわかる。この違いがさささっと把握できる人はすごすぎると思います。

N から逆向きにコストを計算するのに N の隣接頂点を始点にすればいいとはなかなか……全然わからない。そこにやっと気がついても N とその隣接頂点 V を端点とする辺が複数あるということがまた思い出せない。

たいへんだった。これ予選なんですって。

今3ページくらい E 問題の AC 提出があるんだけど、Ruby の 113 ms が最速だった。おもしろ。ただ、前回の言語アップデートから AtCoder のジャッジは、言語ごとに最初の1ケースだけウォームアップ時間が計測に含まれるみたいな雰囲気があるので(このときの話の関連>20230807)、ワースト1ケースのタイムが特異例である場合の比較に意味はない。

こちらの提出 #48433529 を見ると N×M のループを順方向と逆方向の2回回してるだけに見える。辺を端点ごとにまとめたりもしていない。どういうことなの? 結局 L[j]*K(i+1) を掛けるか i を掛けるかというだけの違いだったの? そうみたい。

 提出 #48653380 (AC / 566 Byte / 108 ms)

頂点数を1減らしてコスト計算する試みはけっこう初期にやっていて、でもたぶん他の部分のバグのせいで答えが合うところまでいかなかった。AC コードがある今同じ方針でやってみるとふつーに、よりシンプルに、答えが出た。なんだよもー。さっき書いた 21、22 行目だけど、こうなった。

D1 = D[1,1,E]
DN = D[N,0,ヨ]

そう、これくらい単純に順方向と逆方向の探索ができると思っていたし、実際できるのに、どうして1週間も沼にはまりこむ結果になったのか、謎だ……。


2023年12月16日 (土) [AtCoder] 今日はトヨタ自動車プログラミングコンテスト2023#8(AtCoder Beginner Contest 333)があった。自分のすべての提出。ABCDE の中では C 問題が一番難しかった、というか、N 進数を使ったきれいな解き方がありそうで、でも見つけられなくて、芸のない全列挙をするまでに時間をかけてしまった。E まで簡単。F も解けて然るべき難易度だと思ったけど解けなかった。ではふりかえり。■A 問題「Three Threes」。JavaScript だと文字列に乗算が定義されていないので暗黙的に数値化されるところだけど、Ruby の文字列は乗算が繰り返しになる。■B 問題「Pentagon」。線分の長さは2種類。だけど意外に隣接頂点を見つけるのが難しかった。なんでだろうね。■C 問題「Repunit Trio」。E までで一番時間を使った。制約が小さいから答えが見つかるまで全列挙すればいい。でもしたくなかった。結局列挙したんだけど。敗北。■D 問題「Erase Leaves」。最初は頂点1が葉なら1が答えで、そうでないなら頂点1から生える部分木のうち最もサイズが小さいもの+1が答えだと思った。これはサンプルの3が合わない。そりゃあそうだ。1から生える1つの枝をすべて取り除いてもまだ1から複数の枝が生えているなら1を取り除くことはできない。1から生える最大サイズの枝1本分だけ削除操作が省けるというのが正解。これだと場合分けもいらない。■E 問題「Takahashi Quest」。E 問題にしてはあまりに簡単素直な解法が見えたから、どんな罠を見落としているのか考えてしまったよね。罠などなかった。逆から見てモンスターがいるということを知ってからポーションを拾えばいい。所持数 K を最小化するためにトリッキーな拾い方をする余地があるかと考えてみたけど、そんなものはなかった。結局のところモンスターの直前で対応するポーションを拾うよりましな拾い方はない。■F 問題「Bomb Game 2」。自分が先頭にいて後ろに○人いるときの確率というのを、後ろに0人から順番に求める DP をするのだと思った。元の状態に戻ってくるループがあるなと思った。サンプル1すら合わせられなかった。■■■@2023-12-21 F 問題。Rational でサンプル1を試したらね、1/3 になるべきところが 4/3 になってることがわかってね、計算式に欠けてる項にすぐ気がついてね、そうしたらサンプルの1も2も合うようになりましたね。一度は TLE (#48681588) を出したけど、コンビネーションの計算式を分解してループのあいだ変化しない定数項を外に出したら AC になりましたよ。提出 #48681702 (AC / 574 Byte / 619 ms)。詰めは甘かったけど問題の考え方は間違ってなくて良かった。■ちなみに mod 998244353 の世界において 1/3 は 332748118 で、4/3 は 332748119 なのです。1差。mod の数字を見てもデバッグはできない。


2023年12月10日 (日) [AtCoder] 今日は ABC332 があった。ではふりかえり。■A 問題「Online Shopping」。言われた通りにやります。■B 問題「Glass and Mug」。K がべらぼうな数だと相当にややこしい問題になる。操作1が最も数が少なくてサイクルの起点になりそうだけど、そのときにマグが空とは限らないのがいやらしくて、操作1から操作1までのサイクルごとに初期状態の変遷を考えないといけない。操作1と操作1のあいだに操作2が何回繰り返されるか、そのあいだ、操作2と操作2のあいだに操作3が何回繰り返されるかは固定だけど(追記:マグの初期状態が違うんだから違うか)、最後の操作3の繰り返しだけは少なくなるかもしれない。二分探索をして最後の1サイクルだけシミュレートするとかかな。でもこれは B 問題なので K(≦100) 回操作をシミュレートします。■C 問題「T-shirts」。休日ごとにリセットされるので split(/0+/) した結果の 12 文字列について考えた。1の数を c1、2の数を c2 として、[c2,c1-M].max の max を答えにしたら WA だった。たぶん c2+[c1-M,0].max の max を答えにすべきだった。今以て確信はもてない。2パスの別方針の解答をでっちあげたらそちらはミスがなかったらしくそれで AC をとったので。■D 問題「Swapping Puzzle」。制約が5以下とささやかなので階乗の2乗に2乗を掛けても大丈夫。たぶん賢く操作を構成しようとすると、同じ値を持つマスの扱いが問題になりやすいと思う。すべての並べ替えを列挙して盤面が一致しているかを確かめるのが確実。■E 問題「Lucky bag」。解けてないよ。15 15/1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 という入力に対して3秒弱かかるのでは TLE が避けられない。勘違いする人がいるみたいだけど、3秒弱っていうのはコードテストで 2778 ms (<3秒) かかったって意味だよ。俺は勘違いしてないよ。■F 問題「Random Update Query」。区間適用のセグメント木の香りがします。もっとも、仮にそれが手持ちの道具箱に入っていたとしても関数の合成が書けたわけではない。どこかで読んだけど、Ax+B の A と B を覚えておくだけで合成できたのかな。


2023年12月09日 (土) [AtCoder] 今日は estie プログラミングコンテスト2023 (AtCoder Regular Contest 169)があった。なにか参加者が普段より多かったらしいという話を読んだけど、ABC と間違えた人がいくらかいたんだろうか。自分もすっかり定期開催が当たり前になった今になって開始時刻が 21 時でないことがあると見逃しそう。日記は B 問題のふりかえりのみ。■A 問題「Please Sign」。難しいよね。何を言っているのかわからないからサンプルを頼った。これが 400 点問題。300 点よりは難しいことになっているけども。10 の 100 乗回くりかえすとか、答えが正負ゼロのどれかでいいということから、雰囲気で答えを出していいのかと思ったよね。10 回とか 50 回とか時間が許す限り操作をシミュレートして、A[1] の値の変化量の変化量が正負ゼロのどれかを調べて、正負なら A[1] の値が行き着く先は正負そのままだし、ゼロならどの値で停滞しているかを調べればいいかなと。ダメなんです。無限大無限小に発散していくときはたぶんそれでいいけども、操作によって A[1] が定点を周回するようなとき、打ち消し合わない有限の寄与の軽重を二項係数で計算して合計して、A[1] の値がどこにあるかをしっかり確かめないといけないらしいです。たぶん。これも雰囲気で書いている。■B 問題「Subsegments with Small Sums」。早々にゼロ完を覚悟したけど B 問題が普通の DP で助かった。A 問題があまりにつれなくて 20 分くらいで見切りをつけられたのも運が良かった。連続部分列に切り分けるわけだから、たとえば左から切っていくとして、中途半端に左端に要素を残して得をすることってたぶんない。端から貪欲に切り分けて良さそう? 確信は持てないけどそういうことにして考えた。範囲をだいたいいくつの部分列に切り分けることになるかは累積和で見当がつく。両端を決めてそのあいだにある要素の和が S の何倍であるか。かといって、両端の組み合わせはおおよそ 25 万の2乗通りあって、範囲をひとつひとつ考えることはできないし、範囲のひとつひとつについて、S の倍数をまたぐ境界付近で区切りの位置を確かめることもできない。ところで、異なる2つの範囲について、実は似たようなシチュエーションが現れることがありますね。左端から貪欲に区切りを置いていったときに、同じ位置に区切りを置いたらそれ以降に起こることは共通している。そしてそれっていうのは、共通の区切りを左端とする範囲についての f 値がわかれば計算できる。というわけで数列の右端から、その要素を左端とするすべての範囲について f 値の和を記録していった。範囲の左端を決めたとき最初の区切りがどこにあるかだけ累積和を使って調べた。難しすぎるテストは時間が余って困る法則によって時間には余裕があったので、累積和を二分探索していたのを尺取りに書き換える落ち着きを見せて一発 AC だった。提出 #48318193 (AC / 194 Byte / 119 ms)。べつに尺取りが二分探索でも TLE にはならないんだろうけど、解ける問題を時間内に解くためにシビアなタイムアタックが求められる(そして時間切れになる) ABC との、取り組む姿勢の違いの現れ。■■■A 問題について眠たいことを書いていた。ここに重要な予想していなかった観察結果が書いてあった>「estie プログラミングコンテスト2023(AtCoder Regular Contest 169)参加記 - devgenjin77’s blog」。A 問題が AC できるのはまだ先になりそう。


2023年12月05日 (火) ゲームディスクが出てくるかと期待して漁っていたけど攻略本しか出てこなかったな。体験版をプレイして SO2R をやりたい欲がすごく高まってるんだけど、本をめくってあらためて感じるのは、面倒くさいしもういいか、という気持ち。当時もうやりきっちゃってるので今やっても中途半端なものにしかならない。それでは満足できない、でももう当時のまめさではやりたくない、という葛藤。ピックポケットとかなくなっていた方が嬉しかった。それでもそのうちテキトーにぬるいプレイをすると思う。当時と同じようにレナで敵を殴りまくると思う。だってそれ以外のキャラを操作する理由がないよね。攻撃呪紋と必殺技と剣のリーチがなくてもいいよね(だが CPU にやってほしい回復役を奪っているのが問題)。■@翌日。ディスクを見つけてしまった。disc3 はアストロノーカの体験版だ。アストロノーカは自分のメモリスタックの最大階層を教えてくれる名作……(クリア前に作物のサプライチェーンの管理がオンメモリの限界を超えて投げたという意味)。蒼天の白き神の座とかサガフロンティアとかグランツーリスモ2とかベイグラントストーリーとか FF9 とかガンバァールとかと一緒に出てきた。あるのは知ってたよ。


2023年12月02日 (土) [AtCoder] 今日は大和証券プログラミングコンテスト2023(AtCoder Beginner Contest 331)があった。結果は知らねーよ。自分のすべての提出。では F の精進までを。■A 問題「Tomorrow」。繰り上がり処理を2回まで書けばいい。■B 問題「Buy One Carton of Milk」。おおざっぱな総当たりができる。テキトーに組み合わせて N 個を超えていればコストを比較して最低のものを選ぶ。■C 問題「Sum of Numbers Greater Than Me」。昇順もしくは降順に総和を更新しながら答えを得る。■D 問題「Tile Pattern」。はい、G 問題を除けば本日のボス問題です。いや、まあ、ただの2次元累積和なんだけど。図を書いて整理すればもっと早く解けたのかな。クエリを2段階に変換した。クエリは左上隅の点と右下隅の点として与えられる。用意した累積和は原点からの和なので、いつもの包除で A-B-C+D の形にする。A,B,C,D は原点を左上隅とする矩形(に含まれる黒マスの数)なので (高さ, 幅) として解釈してもいい。これを N×N の正方形の繰り返しと、(N の倍数)×(半端) と (半端)×(N の倍数) と (半端)×(半端) の長方形3つに切り分けて N×N の累積和から数字を拾う。これに1時間 12 分かけたんですって。それってコンテスト時間(100 分)の 72 %。E、F 問題を解く時間はどこだ。■E 問題「Set Meal」。N×M の組み合わせを全列挙はできないから、うまいことコスト順に列挙して駄目ペアではない最初の組み合わせを見つけたい。最初はうまく列挙できなかったけど、開き直ってあまりうまくない列挙にすることで列挙しやすくなった。プライオリティキューに最初に N+M 個のアイテムを突っ込んだ。各主菜に対してもっとも価格の高い副菜を、各副菜に対して最も価格の高い主菜を、まずはキューに入れた。あとは取り出しては次の候補をキューに入れる。そして駄目ペアでなければ答えにする。駄目ペアの持ち方について。添え字で与えられるが数列をソートすると情報に齟齬が出る。だがソートはしたい。添え字配列をソートすることで主菜副菜のソート列と駄目ペアの添え字を仲介したけども、駄目ペアを値のペアとして持って数を数えても良かった(その場合自分がやったように重複した組み合わせをキューに入れてしまうと間違えるんだけど)。そんなとこに気を回してる余裕はなかったけども。結局 35 分かけてコンテストは終わっていた。■F 問題「Palindrome Query」。ローリングハッシュかな。1点更新のセグメント木かな。ちょうど何日か前に第16回 アルゴリズム実技検定(過去問)-N「ソートと関数」に提出した #48039671 が使えそうだな。TLE (#48152176, #48153685) が出た原因は累乗をメモしなかったことと、選んだ素数が 51 ビット幅だったことではないかと思う。51 ビットと 51 ビットを掛けたら AtCoder のジャッジサーバーで動いている Ruby の整数型が 64 ビット幅だとしても Bignum が出てきて遅くなるんだよ。提出 #48153886 (AC / 1130 Byte / 1164 ms)。■最近の ABC は F 問題まで解ける問題が並んでるんだけど、成績がまったく奮わないのなんでだろね。■■■D 問題。最初の提出 #48139371 (AC / 520 Byte / 1188 ms) は紆余曲折を反映して使っていない変数や意味のない処理が残っていたので整理した。提出 #48191813 (AC / 404 Byte / 725 ms)。最初にすっきり見通しが立っていれば 10 分で書けるようなスクリプトだ。かけた時間(72分)の言い訳はできない。■■■最近の結果をふりかえると取り組み方について考えてしまう。A 問題から順番に解くことについてだとか、考えを詰める前に実装を始めて迷走して結果的に初期のコーディング時間が無駄になっていることについて。コーディングをすることで細部が煮詰まっていったり誤りに気付いたりする側面があるので、一概にすぐに手を動かし始めることが無駄というわけではないんだけど、でも、コーディングという思考補助なしで考えを詰められるようになれば、将来的に時間が節約できるということがあるかも。30 分は手を動かさないとか、F 問題まで解法の最後まではっきりさせてからコーディングを始めるとか、落ちようがないくらい低成績の今だからこそやりやすい。


2023年11月28日 (火) [AtCoder] 精進。第15回 アルゴリズム実技検定(過去問)-N「度数分布」。中央値と平均値を近づける限界はどこにありますかという問題。ど忘れしたけど、よくある標準的な分布だと中央値と平均値は一致するけど、分布が対称でなければ両者はずれる。度数分布が C 数列として与えられる。どうしますか。まず中央値がどこにあるか決めたい。これは項数の偶奇によらず幅1の範囲に決まる。平均値は実数 x を階級の幅1の範囲内で移動することで操作できる。中央値より左にある値は右端に、右にある値は左端に寄せることで平均値が中央値に近づくだろうか。そんなことはない。ある階級に a+1 個の値があり、また別のずっと離れた階級に a 個の値があり、N=2*a+1 のとき、中央値と同じ階級にある a 個の値は中央値から最大限離れ、別の a 個は中央値に最大限近づくのが平均値を中央値に近づける。どうすれば最適な結果が得られるのか。中央値を決め打ってありうる平均の最低と最高を求めてその範囲と中央値の差を調べた。この前の ABC330-B「Minimize Abs 1」と同じ問題。三分探索で底のありかを探った。時間制限があるので階級をさらに3つのクラスに分けて単純化した。即ち、中央値を定める値の存在によって一方の端に寄せられない階級と、その左または右にあって自由に値を選べる階級の3つ。実は左右のクラスを区別する必要がなかった疑い。項数が偶数のとき中央値を定める値は2つあり、この具体的な値の組み合わせは無数に考えられるけど、2数の差を最小にするのが、他の項がとりうる値を制約しないという点でベストと決まっている。■提出 #48014051 (AC / 892 Byte / 195 ms)。


2023年11月27日 (月) 冬は着ぶくれするのが面倒くさい。たとえば上半身にアウター、シャツ、ウールのTシャツ、肌着の4枚を着て、下半身にズボンとウールのタイツと下着の3枚をはいていたりする。この4×3の組み合わせの重ね合わせを適切に保つのが面倒くさいなあと考えていたときに、ちょっと待てよと気がついたのだった。レイヤーが全部で4+3の7枚あって、そのうちの4枚がどのレイヤーに位置するのかを決める、または3枚がどのレイヤーに位置するのかを決める重ね方(※どちらも同じ数になる)は、7C4 = 7C3 = 35 通りであって、4×3 = 12 通りではないのではないかと気がついたのだった。トイレに入って出るだけで面倒くさいことだなあ。


2023年11月26日 (日) 『なっとく!関数型プログラミング』(Michał Płachta (原著)/株式会社クイープ (監修)/株式会社クイープ (監修) / 翔泳社) という本を読んでいて、値としての IO という考え方に初めて触れたのだけど、これって Node.js でどこまでも async に追いかけられる感覚に似ているなと思いました(小並感)。一度 async にしたらどうやって sync に戻れるのかわかりません。たぶん IO くんもどこまででも追ってくるのではないか。俺ってイミュータブルな値だからお前の純粋性を失わせたりしないぜって言いながら、関数型コアの深いところあらゆるところまで入り込んでくるのではないか。この、何か(っていうか実行)を保留にしながらどこまでもひっぱりまわすもどかしさすっきりしなさメタなややこしさに耐えられる気がしない。


2023年11月25日 (土) [AtCoder] 今日はトヨタシステムズプログラミングコンテスト2023(AtCoder Beginner Contest 330)があった。自分のすべての提出。結果はまあいいや、F 問題の精進を日記に書けることに気持ちを良くしてふりかえっていこう。■A 問題「Counting Passes」。Array#count■B 問題「Minimize Abs 1」。問題文が難しすぎる。数分かけてじっくり根気よく投げ出しそうになる気持ちを抑えながら理解する必要があった。要するに、ある範囲(L-R)に含まれる整数のうち最も A[i] に近い数を、A[1] から A[N] について答えよ、という問題だった。ループと配列はいらなかった。繰り返しなしでも十分に B 問題だったのに、ループがさらにややこしくしていた。答えの候補は {L,R,A[i]} に限られるんだけど、謎に {L,R,A[i]-1,A[i]+1} を候補にしてペナルティ5分。根気が尽きていた。■C 問題「Minimize Abs 2」。平方数を2つ足してある数 D にどれだけ近づけられますかという問題。図形的な意味があるかは知らない。大きくなりすぎない範囲の平方数を列挙して調べた。■D 問題「Counting Ls」。ちょっと方針に迷った。グリッドをスキャンしながら2種類の累積和を更新しつつ数えていけるかと思ったけど、欲しい数が足りていないことに気がついて頓挫した。正解方針はこう。最初にある行またはある列にある o の数を数えておく。その後もういちどグリッドをスキャンして、ある点が L 字の角にあたる場合の数を数えて足し合わせて答えにする。ところで、@kyopro_friends さんは頓挫せずに解ききってしまったようですよ。「フェネック「アライさんは最初このことに気づかずに、L字を置く向きを4通り試す実装をしてるんだけどねー」 アライグマ「そのことは秘密なのだ!」」■E 問題「Mex and Update」。デバッグ時間が1分14秒足りなかったせいでこれは精進です。こいつ先々週も似たようなこと言ってたな(「なんてことのない F 問題を通すのに2分6秒ぽっち足りなかったのがくやしい」)。セグメント木を使って該当する数のうち最も小さいものを探す。値の上限が 10^9 だということで座圧したのだけど、そのせいでバグらせたケースが 3 1/0 2/1 0 みたいなの。クエリでは数列を書き換えていないので {0,2} の MEX である1が答えなのだけど、座圧しているせいで1番小さい値(0)と2番目に小さい値(2)のあいだに隙間があることに気付けなかった。+1 した値を加えておおよそ2倍の数の数字を扱うことで AC になったのだけど、バグの原因を見つけるのに手間取って時間が過ぎてしまっていた。Ruby での他の提出より倍くらい遅いみたいだから、もう少しスマートな解決法があるかも。■F 問題「Minimize Bounding Square」。終了後に問題を読んで、根を詰めずに休み休み考えてだいたい1時間とちょっとで AC になった。時間内に解けた可能性はゼロなので悔しさはなく解けた喜びだけがある。まず X 座標と Y 座標に問題を分ける。X 座標(Y 座標)の幅をどれだけ狭められるかは左からの累積和と右からの累積和でわかる。Sandwiches (20230902) と同じ要領で、座標の和と個数から操作回数が求まる。ところで、全部でいくつ狭めるかが決まっているときに、右からいくつ狭めるかと左からいくつ狭めるかは貪欲にステップを刻んで決めることができない。1狭めるだけなら左から狭めた方が得なケースでも、3狭めるときにはすべて右から狭めた方が良くなるケースというものが考えられる(※)。N≦20万なので答えを二分探索する中で線形時間のスキャンをしても大丈夫。2秒を 67 ms 超えたけど(#47948117)、制限時間が3秒だったのでセーフ。■Twitter の成れの果ての何かを眺めているとギリギリで E が通っていても緑パフォだったらしいとわかる。じゃあなんにも惜しくねーな。そのラインは伸るか反るかの最前線よりずっと下だ。■F 問題についてすごいツイートを見かけた。「FのC++の最短コードを見てる atcoder.jp/contests/abc33… X/Y座標を長さLの中に包含させる最小の操作回数は外側から順にペアを作ったときに max(ペアの長さ-L,0)の総和になる ってことだと思うけど頭いい」。なんでそれが答えになるのか全然わかんない。■※ 嘘を書いていたっぽい。貪欲に狭めて良い……らしい。「アライグマ「いま左右の端にある点のうち少ない方を全部1個となりに詰めるのが最適なのだ。ということは答えの関数は、こういう感じの折れ線になるのだ。元の2次元の問題なら、x方向とy方向で解いて足せばいいのだ!」」。考慮してその結論が間違ってるなら何を頼りにして答えが導き出せるというのだ。■■■E 問題。座圧したときの値の抜けは落とし穴というよりも答えへのショートカットだ。A 数列に存在せずクエリで設定されることもない値は常に MEX として選ばれる可能性がある。そういう値の最小値が答えの上限になる。問題に対する総合的な理解が足りていないから見落としてバグらせる。提出 #47966425 (AC / 1211 Byte / 642 ms)。BIT で MEX 候補の上限までを座圧なしで管理したら 1973 ms だったのが 642 ms まで早くなった。■F 問題。ゴルファーの理解できない解法はさておき貪欲に四辺を内へ寄せる解法を実装してみた。提出 #47975884 (AC / 976 Byte / 642 ms)。2067 ms だったのが 642 ms まで縮んだ。クラスを使ってコードの重複をできる限り省いたつもりだけど、それでもかなーり面倒くさい実装になっている。


2023年11月21日 (火) 新聞のコラムでムーンウォークとななめ立ちの具体的描写を読んで、それが先日『プロトコル・オブ・ヒューマニティ』(長谷 敏司) を読んだところだということもあって、そうかマイケル・ジャクソンってダンサーだったんだなと腑に落ちたのだけど、すぐに数行前の冒頭で「歌とダンスで世界を魅了したマイケル・ジャクソン」と紹介されていたことを思い出したのだった。そもそもマイケル・ジャクソンを知らない人がいるとも思えないし、自分も名前と一種異様な姿を映像で見たことはあるけれど、知った最初からすでに伝説的な存在であり断片的な映像が目に入るばかりだったから、本業を真正面から見たことはなかった。実はマイケル・ジャクソンについて知っているようで知らなかったし、それ以上にダンサーとはいかなるものかということを全く知らなかった。おそらくダンサーなるものとのほとんど唯一といえる接点は『月の子』(清水 玲子) の主要人物アートがダンサーだという、その程度のものでしかない。だから『プロトコル・オブ・ヒューマニティ』においてダンスを含む小説世界がバシバシと堅固に言語化されていく様は非常に気持ちよく新鮮だった。そうだったのかという発見と納得の連続だった。著者の人はあとがきで専門家への配慮から「くわしいかたからは、言いたいこともおありかと思いますが、ご容赦を」と書いていたし、十割からの減点法で言いたいことが当然に出てくるのかもしれないけど、素人である自分にしてみればゼロだった理解が9割に引き上げられたくらいのつもりでいるので、そこは誇ってほしいと思う。小説主人公の父親をきっかけとする中盤からの流れも見事に統合され、「今年のベスト1冊」におさまらない1冊になっていた。あとがきが「今、キャリアの中でも特別なのではないかと感じる作品を、ちょうど手離れして、みなさんのところにお届けしようとしているところです」から始まるのだけど、これがうぬぼれでも誇張でもないことに読者の立場から完全に同意する。すごかったんだよ(語彙力)。


2023年11月18日 (土) [AtCoder] 今日はSky株式会社プログラミングコンテスト2023(AtCoder Beginner Contest 329)があった。自分のすべての提出コンテスト成績証。ABCDF の5完で低め安定といった成績。E 問題がおもしろかった。そしておもしろい問題にはやられてしまう。ABC って基本的に筋トレだと思うんだよね。反復練習であり、反射神経とタイプ速度を競っている。どの問題までがそうかというのは人それぞれだけど、自分の場合は D 問題までは常にそう(本当に?)。E、F 問題はどちらとも言えなくて、だけど解けるものは典型度が高め。だから昨日の E 問題みたいなのにはやられる。ではふりかえりと精進を。■A 問題「Spread」。はい。■B 問題「Next」。罠がありますね。最大値が複数あるときに1位タイの値を2番目の数として出力してはいけない。サンプルがちゃんと仕事をしていたので罠ではあるけどひっかけではない。■C 問題「Count xxx」。14 分かけました。ループで書くことはできるし、各アルファベットごとに単純化した問題を解くこともできる。でも書きたくないと思ってしまった。String#scan メソッドだとか /(.)\1*/ みたいな正規表現パターンでうまくできないかとあれこれ考えて、できなかった。同じ文字の繰り返しを意味するパターンって難しくない? キャプチャを使うとできるけどそうすると String#scan メソッドが役立たずになるので困ってしまった。■D 問題「Election Quick Report」。D 問題で BIT とかプライオリティキューとか使いたくないよね。考えました。最高得票者が入れ替わる瞬間がつかまえられさえすればいいので、誰が最高得票者で得票がいくつかがわかればそれでいい。■E 問題は解けなかったのでひとまずとばして F 問題「Colored Ball」。ただのマージテク。考える時間も書く時間もいらない。E 問題を考えていたせいで F 問題の AC が 30 分近く遅れたのを挽回するためには、最終的に E 問題も通すほかなかったんだけど、結果はあのとおり。残念無念。■■■E 問題「Stamp」。難しかったよね。時間内のこの提出 #47726211 (WA×5) だけど、19 行目が嘘貪欲なのはわかっていて、でも残り数分で新しい方針をでっちあげることもそれを実装することもできない。昨日はもうお手上げだった(あ、この日記は翌日に書いています)。■提出 #47752502 (AC / 515 Byte / 393 ms)。これは翌日の今日なんとなく転がしていたアイディアを実装したもの。貪欲なのは同じ。違うのは起点が複数あること。昨日は左右端を起点にしてスタンプを上から押したり下に差し込んだりする操作を考えていた。今日は T と完全一致するすべての場所を起点にして、そこから(端でなければ)左右にスタンプを繰り返し差し込む操作を考えた。操作の起点を複数考えることでスタンプを上から押す操作を考えなくて済んだのが昨日との違い。複数の可能性を考えなくてよくなり貪欲法がはまるようになった。最終行の判定がやや複雑で条件が3つある。両端の条件に2つで、貪欲法と貪欲法の狭間に残る部分の判定に1つ。■たしかに D 問題とのあいだで配点はひらいているけども、これを E 問題に配置しようって判断が、それもあの F 問題の前に配置しようって判断が謎すぎる。でも考えるのが楽しい問題だった。知識問題というよりのーみそこねこね(コンパイル)な問題だったのでは。