/ 最近 .rdf 追記 設定 本棚

脳log[2024-08-24~]



2024年08月24日 (土) [AtCoder] 今日は日立ヴァンタラプログラミングコンテスト2024(AtCoder Beginner Contest 368)があった。ペナルティでぼろぼろになりながらすべりこみで ABCDE の5完で満足してるんだけど、E と同配点の F が通されすぎでは?■ A 問題「Cut」。pop(K) かなと思ったけど、出力のしやすさから rotate(-K) にした。irb で確かめたら rotate(K) ではなかったから rotate(-K) にした。下から K 枚といういやらしい設定はちゃんと読めていた。■ B 問題「Decrease 2 max elements」。偶奇によらず A.sum/2 で間違いないことを確かめたと思ったらサンプルの2に殺された。場合分けをして祈りながら出した。こんなにはらはらさせられる B 問題はそうそうない。あ、シミュレートできる制約なんだ。無駄に考えてしまった。でも 100×100×100=100万のオーダーだと見積もるのも簡単ではないね。■ C 問題「Triple Attack」。シミュレートします。その際に、1周期(T を3増やして体力を5減らす)を超える処理は周期数を数えて一括して計算します。サイクルはどこから数えても1周した結果が同じだということを都合良く利用します。■ D 問題「Minimum Steiner Tree」。シュタイナー木とは見かけた名前だけど中身は知らない。調べませんよ。頂点を輪っかに並べて最小限の頂点と辺を選ぶアルゴリズムがあった気がしたけど、覚えていない。調べませんよ。テキトーに根を決めて、深さを計って、最も深い所から浅いほうへ向かって、必須の頂点を種にして、1つだけの共通祖先に行き着くまで親を取り込んでいくようにした。根に収束していくので頂点数程度の操作回数。テキトーに根を決めていいのか考えたけど、木は頂点間のパスが一意に決まるのでどこが根とか関係なさそう。終了条件を間違えて1ペナ。無条件にテキトーに決めた根までさかのぼってしまっていた。中断するときは、最も浅いところにある指定された頂点を取り込み忘れないようにする。X で見たけど、テキトーに決める根を V1 なり Vk なり必須の頂点の1つにしておけば何の面倒もなかったらしい。それは……頭がいいね。調べないと書いた2つ目は Auxiliary Tree という名前だったと X で見て思い出した。読めない名前はそのぶん手がかりが少なくて思い出しにくいかもね。そしてこれはこの問題には不適だったとも書いてあった。■ E 問題「Train Delay」。問題文が難しい。サンプルも利用して理解したところでは、ある1つの電車が遅延したとき、遅延によってできたはずの乗り換えが不可能にならないように、連鎖的に後ろの電車を遅延させていくと、遅延の合計はいくつになりますか、というもの。答えに選択要素はなく、無駄なく遅延させていけば答えになる。効率的にやるのが難しい。最初は電車の組み合わせを考えた。ある駅で発着する電車と電車で相互作用をさせる(相互ではない)。30 分かけて書いたこれは TLE だった。遅延の伝播をアドホックにやるのではなくトポロジカルソート順にやるのかと思って5分で書き直したものは、1つだけ TLE が減ったけどまだ TLE だった。たとえば半分の電車が駅1を発ち、半分の電車が駅1に着くとき、電車と電車の組み合わせは M^2 のオーダーなのだから、TLE はしかたがない。こういう、ダメな理由は、いざ TLE が出るまで考えないのです。途方に暮れていた時間を含めて 25 分でイベントソート版をでっちあげたら、総取っ替えの大手術だったにもかかわらず、TLE なしの WA×2 で筋が良い。その後5分で AC に至る。終了1分前。WA の原因は同時刻の発着を必ずしも着→発の順に処理していなかったこと。着イベントと発イベントを時系列順に再生しながら、駅ごとに最後に到着した電車の時刻(遅延込み)を記録しておくだけで伝播する遅延を確定していける。提出 #57090926 (AC / 397 Byte / 1847 ms)。昨日の日記(20240823)に「イベントを時系列順に並べることで考えるべき対象が限定できる」と書いていたことを思い出せたのが打開のきっかけ。■ F 問題「Dividing Game」。配点が同じなので最初に実装を始める前に F 問題まで問題を読んでいた。「ゲーム、Nim、Grundy 数、うっ、頭が……」ということだけ確認してから A 問題からの実装を始めた。F 問題が簡単だとは言わない。自分には解けない。答えに至る論理の道筋を納得しながら辿ることができないので。Nim の必勝法とか XOR のあたりで論理がお隠れになる。だから他の人も同じように解けないでいて欲しいのだけど、順位表を見ると 2454 人が通している。それはおかしいよ、俺に優しくないよ。ちなみに E 問題の方は 327 人。問題文が難しいし長かったからね、読みたくないからしかたないね。■自分のすべての提出。■精進……失敗。G 問題「Add and Multiply Queries」。前々回の F 問題 Maximum Composition を思い起こさせる設定。セグメント木なのかな、自分には乗せられないなと思っていたけど、「A,Bは正なのと、3つ目のクエリの条件より、区間内にB[i]が2以上な物は高々log(10^18)個程度しかない。B[i]=1の部分は、v+=A[i]をした方が良いのは明らか。よって各クエリに対し、B[i]=1の部分はBITを使ってA[i]の和を一気に加算し、B[i]>1のところだけ加算と乗算どちらを取るか両方試していこう。」というのを読めば自分でも実装できる。提出 #57100689 (TLE×1)。こんなのってないよ。■最近タイプミスが多すぎるんだけど、たぶん爪が長い。白い部分が長いもので 6 mm ある(モニターの前に裁縫用のメジャーがあります)。短いのは 0 mm。むしろマイナス。ついつい噛んだり裂いたりしてなくなってしまいがちなので、触らないようにして最近は保護が行き過ぎている。長い爪というのは他の爪を攻撃しやすいのであり、また、長い爪というのは側面のちょっとしたささくれからぺりぺりと裂かれやすいのであり、攻撃力が高く防御力は低く、たいへん失われやすいものであるので、衝動を抑えていられる限りアンタッチャブルにしておきたいと思っているが、不便だな。ということをこの場所に書いているからには、不便を感じる機会が他にはあまりないということなんだな。■■■精進成功。G 問題。提出 #57153896 (AC / 918 Byte / 1161 ms)。配列の全長が高々 60 だとわかっていれば、長さ N の仮想配列に対する対数オーダーの処理を提供する BIT を使う必要はなくて、普通の配列に線形オーダーの基礎的な操作を施す方が定数倍がいいという話。というより、「次」を見つけるのに対数時間ではなく定数時間で済むという付加価値が大きいのかな。これでネタバレを恐れずに Ruby で唯一の AC だった konayuki さんの提出 #57098548 (AC / 1861 ms) を見に行ったら、クエリ3の答えをメモしていた。そう、1ケースだけだから、どういうクエリに弱いかを特定して対処しようと自分も考えていた。でもテストケースが見られないとなかなかわからないよね。■ちょっと勘違いをしていた。クエリ3の計算結果に制約があるのであって、1<Bi (i=1..N) なる要素の数が制約されているわけではない。だから B 数列の2以上の要素を管理するのに BIT ではなく Array を使うのにはリスクがある。ほとんどがクエリ2であるテストケースがあったら O(QN) で TLE だった。


2024年08月23日 (金) [AtCoder] 精進。CodeQUEEN 2024 決勝の ABCDE。配点の通り ABC の ABDDEFFG みたいな問題セットだったと思う。6問目は解けてないし7、8問目は問題を読んでもいないけど、5問目までは ABC の ABDDE とか ABDEE みたいな感じの難しさ。■ A 問題「Welcome to AtCoder Land 2」。参加者に必ず何かを持ち帰ってもらう問題。提出 #57008363 (AC)。■ B 問題「Decode Time Signal」。こういう問題は、問題文で与えられたモールス符号の表をいかに手を加えずに利用するかだと思う。プログラムというのは可変部分をデータファイルや設定ファイルに押し出して、最小限の共通部分をコードにすることで、複雑さを抑えながら最大限の表現力が得られるもの。符号表を手作業で Ruby スクリプト(Hash リテラル)に加工するようなことは避けたい。提出 #57008436 (AC)。そういうお前は %w[...] リテラルに加工したけどな。■ C 問題「Attraction on Rainy Day」。配点は ABC-D。30 分かかった。KN≦2000万だから O(KN) が通らない可能性があって、余分に考えてしまった。DP なんだけど何をキーにして何を記録するのかひと目ではわからない。変数は「現在時刻」「濡れた量の総和」「アトラクションに乗った回数」。時刻ごと乗った回数ごとに濡れた量の総和を最小化する DP をする。スタートとゴールに近いときは考えるべき乗った回数が制約されるので、そこでループの回数をケチった。提出 #57009112 (AC / 1387 ms)。■ D 問題「Attend Many Events」。450 点。難しかった。30 分プラス2ペナ。頂点と辺とイベントの数がいずれも 1000 以下なので、いずれか2つの組み合わせが 100 万のオーダーで許される制約。イベントに参加するしないを総当たりはできないけど、イベントを時系列順に並べることで考えるべき対象が限定できる。つまり、あるイベントに参加するというとき、人の位置と時刻がそのイベントによって固定される。そのイベントに参加できるかどうかは、最後に参加したイベントもしくはスタート地点との距離によって決まる。過去のイベントを見て参加可能かどうか、それまでにいくつのイベントに参加しているかを見て最大のものを選んで現在のイベントに第3のパラメータとして記録する。O(KK) で許された。2地点間の距離は予め求めておく。2乗のアルゴリズムが許されるのでダイクストラ法を使わないで BFS/DFS でもたぶん大丈夫。提出 #57009717 (AC / 274 ms)。一度 WA を出した原因は、過去のイベントの中にはどうやっても参加不可能なものがあるということを適切に取り扱えていなかったこと。イベントに参加できていなかったのなら、その時刻その地点に居たという事実が存在しない、ということをきちんと反映させる。ところで、2乗のアルゴリズムを全頂点で実行したら3乗になってやばいのでは? なんで全点間距離を愚直に求めて許された? だからこその 1000 以下という、次の E 問題より小さい、2乗掛ける log が許されるやや小さめの制約か。■ E 問題「AtCoder Hotel」。どの制約も 5000 以下ということで、2乗の DP が許されたり TLE になったりしそう。何をキーにして何を記録する DP をしようか。結論を書くと C4B という変数名がすべて。つまり、B 人分の定員を確保するために C 円払ったということを記録する。B がキーで C が値。C を最小化する。ランクはどうなった? これは人をランク要求で、客室をランクでソートすることで暗黙的に処理できる。客室ランクは大が小を兼ねるので、最もランク要求が厳しい人を満足させられる部屋だけで DP を行ったなら、その結果は次の人に対しても通用する。ただし、前の人が収まっているのでキャパシティが1減っている。提出 #57010115 (AC / 657 ms)。収容可能な人数として考慮すべき最大が徐々に減っていくのを考慮することでループの回数をケチった。C 問題と似たことを書いてるけど、こちらは 20 分しかかけていないので、C より E の方が簡単か。■ F 問題「Divide the Cake」。求められている答えから、更新しながら順番に判定をして最初の答えを見つけるんだと思うけど、どういう形式で記録すると高速に判定と更新ができるのかわからない。


2024年08月17日 (土) [AtCoder] 今日は AtCoder Beginner Contest 367 があった。最終的には嬉しい結果だったけど、かけた時間で難易度を判定すると E<F<<<<D なのが複雑な気持ちにさせる。もっと早く D が解けても良かったのでは? 以下ふりかえり。■A 問題「Shout Everyday」。スマートに解くことができない。ループを回して %24 で現在時刻を表し、それが A 時ならたこ焼きへの愛を叫び、それが B 時ならループを終了した。■B 問題「Cut .0」。String#chomp! という便利なメソッドがあるので、無条件に5回呼び出した。■C 問題「Enumerate Sequences」。5の8乗掛ける8が数百万のレベルだったので、Array#product で全数列を列挙して選んで出力した。■D 問題「Pedometer」。問題文を読んでもいない G を除けば本日のボス問題。やり方は少し考えるとわかった。最初に累積和を用意すれば、頂点1から自身を除く他の N-1 個の頂点までの距離の一覧が得られる。この一覧を更新しながら、全頂点について、条件を満たす他の頂点を数えるのが方針。条件というのが M の倍数となっているけど、デバッグのためには %M が邪魔なので、うまいことできればいいですね! 自分は AC まで 51 分かけて、そのうち 40 分かそこらは下手なデバッグに費やしていた。■E 問題「Permute K times」。一読ダブリング。■F 問題「Rearrange Query」。Range Set Query とかそのへんの問題に見える。一致判定はソートして一致するかどうかでいいが、クエリごとにソートすることはできない。残り時間が 25 分しかなかったので(D 問題の半分!)、半ばあきらめて仰向けになって目をつぶって考えてみると(今日は朝がいつもより 30 分早かったのだ)、ローリングハッシュでずるいことができそうだと思った。数列の各値を素数で置き換えて、範囲の一致を範囲の積の一致で判定するならソートの必要もない。実装が軽かったので最初の提出を出したのが終了7分前。これは要素を素数に置き換えるためのコードを呼び出し忘れていたので WA になった。2分で修正して出した2つ目の提出は、素数1と素数2をあちらこちらで取り違えていたのが原因で WA だった。さらに2分で修正して出した3つ目の提出が AC だった。終了3分前。これが WA だったときのために3つ目の素数を利用するバージョンを WJ のあいだに用意していたが、結果的に必要なかった。■自分のすべての提出。前回が3完で、今日も D 問題がさっぱり合わなくて泣きたくなるやら腹立たしいやらだったけど、最終的に A-F の6完で大満足。D 問題がもっと早く解けたはずだとか、F 問題の2ペナの原因がしょうもなすぎるとか、欲をかけばきりがないけども、冴えないなりに健闘した。■F 問題。他の提出を見てると、自分はずるのしかたが下手だった疑いがある。素数はたくさんはいらないのかな。要素を素数にするのでなく、要素を素数の指数にして、範囲の和の一致で判定をするのが、まっとうなずるのしかた、っぽい? チェックサム? ローリングハッシュ自体がベースと mod の2つしか素数を使わないしね(※2つの素数でも十分だけど、書いてあったのは互いに素な2数だった)。自分みたいに要素をそれぞれ異なる素数に置き換えて、それらの範囲の積で判定をするのは、無駄だし要素数に応じてスケールしない下手なやり方。そもそも、ロリハ言いながらなぜ変なやり方をしているのか。たぶんローリングハッシュは位置と値に意味があるもの(位取りされた数字の列や文字列)に限られた範囲のアイデンティティを割り当てる方法であって、F 問題で必要なのは多重集合のアイデンティティだから、ローリングハッシュとして知っている方法にならなかった。各要素に散らばったユニークな値(x 番目の素数であったり、素数の x 乗であったり、Ruby であれば x.hash でも OK らしい)を割り当てたら、集合のアイデンティティとしてはその和でも積でも求めれば順序を問わない値が合成される。発想はロリハでもそれはロリハとして知っている方法ではなかった。ところで、「x 番目の素数」と「和」の組み合わせでは衝突しやすいかもしれないので(2+5=7 みたいに)、素数の積にしたのはファインプレーだったのかテストケースの穴なのか。あ、素因数分解から連想して ID を定めるか、N 進数から連想して ID を定めるかという感じでまとめられるのかも。素因数分解ならあいだに入る演算は掛け算に決まってるし、N 進数なら当然各桁重み付きの和を求める。多重集合の要素に割り当てる値の定め方に応じて、合成された全体の ID として積が選ばれるか和が選ばれるかは決まっていたのかも。■D 問題。不明なまま放置していた問題名。ペドメーターって響きがいかがわしいけど(それはお前が……)、pedestrian (歩行者) から連想すると歩数計なのかな。読み直すと、問題を解くのには全く影響しない単位が歩数だったから、たぶんそう。あたりをつけただけで確定はさせずにやっぱり放置するのはいつも通り。pedometer が何かに興味がないし気にもならない。広告があふれすぎている現代への適応。■■■ D 問題。「累積和を計算して、Mの剰余が同じ位置を集めます。そしてその位置の集まりで差がN以下の個数をカウントします。そのとき、その集まりが大きくなる可能性がありますが、尺取り法的に計算すれば速くなります。」を参考にして。提出 #56958498 (AC / 318 Byte / 371 ms)。最初の提出 #56820856 (AC / 232 Byte / 202 ms) より長くて遅くてメモリ消費量も多いけど、ややこしいデバッグは必要なかった。コーディング時間は 20 分くらいかな。30 分の節約。


2024年08月15日 (木) THERMOS の真空断熱マグを新しくした。新しいのは JON-481 STG で、古いのは JMK-500 BK。調べたら古いのは 2008 年の 10 月に購入していた。16 年前だ。理由は最近になって頻繁に漏れるようになったから。一度パッキンを新しくしているし、栓とパッキンのセットも一度新しくしているけど、もう消耗品が手に入らない。仕様の違いによりいろいろと得失がありますね。プラスチックの飲み口と、金属の飲み口。栓の大きさによる栓を介した冷めやすさと飲み口の広さ。2ピース構造の栓はパッキンが2つあって、潜在的な漏れ要因と洗浄パーツが増えるけど、傾けたときに(しっかり保温されていて熱いかもしれない)中身が飛び出してこないような仕切りを設置することができていた。新しいものは栓1パッキン1と簡略化されているが、一長一短というところだと思う。ところで、栓とパッキンが一体であることをうたうものもサーモス含む複数メーカーから販売されていた。それはやりすぎだと思う。あるメーカーのものは、どうやって洗うのかがわからなかった。今まで使っていたもので苦労していたから知っているけど、細い隙間に茶渋を含んだ液体がたまる。メンテナンスのしやすさを考慮していない道具は信用できない。サーモスの栓パッキン一体タイプは、一体といいながら一部を取り外して丸洗いできるメリットが強調されていて、洗うことを考えている点でその点が不明だった他メーカーより信用できるが、とあるレビューで自転車に取り付けるとカチャカチャうるさいと書かれており、まさしく一長一短だなと。そもそも栓パッキン一体が失敗だったのだ。パッキンだけなら 200 円程度だが、栓パッキンだと 800 円する。パッキンの抗菌防カビは実際に意味があるが、それにも時効がある。一度生えると増えるのは早い。ライフサイクルが異なるものを一体不可分にするのは設計判断としてどうか。ネジ式とワンタッチ式にもそれぞれの得失がある。形状の複雑さからくる洗いにくさと壊れやすさ、漏れへの不安から、便利さを全く評価しない自分はネジ式一択になる。必要ではないからこそ便利だという評価になっている。タダで手に入る便利さなら結構だけど、一長一短の便利さは不要。ところで、新しい方はてっぺんほどすぼまったテーパー形状で、筒も細身だ。新旧で容量が同じで長さもほとんど違わないのだから、細いのは製造技術の進歩なのかもしれないが、径の小ささとテーパー形状とフタの薄さが合わさってとても開け閉めがしにくい。力がいるし、滑りやすい。古い方は径が大きいのもあるし、頭が張り出した逆テーパー形状なのもあって、開けるのに握りやすいし、提げるのにすっぽ抜けたりしなかった。このフタの形状こそタダで手に入る便利さの例だと思うんだけど、どこにコストを見出して以前は具えていた使いやすさを捨てたというのか。ものを作る人間がおしゃれ感のために人間工学を捨てるようなことがあれば軽蔑するよ。何も捨てずにかっこいいものを作るんだ。


2024年08月12日 (月) [AtCoder] X で見かけた。「AtCoderでずっと不満な点として言及されてるの見たこと無いけど 「998244353で割った余りを…」 は、 「素数998244353で割った余りを…」 って書いて欲しい。 書いてないということは、これが素数であることを覚えゲーとする、あるいは調べろってことであり、その意図が分からない。マジでなぜなの?」 わからない主張ではない。最近は 9..... だけ見て「はいはいいつものね」と油断した対応をしているけど、初心を忘れていなかった頃は、irb を開始して require'prime' して 998244353.prime? #=> true を確認していた。この数字をまともに読んだことも入力したことも一度もない。ずっとコピペしてる。覚えゲーは成立しないのです。だから素数って書いてあると嬉しい。


2024年08月10日 (土) [AtCoder] 今日は AtCoder Beginner Contest 366 があった。くるしい。■A 問題「Election 2」。T と A の大きい方が N/2 (切り捨て) より大きいかどうか。■B 問題「Vertical Writing」。すごーくめんどうくさいけど、まあまあ面倒が少ない実装ができたと思う。まず、短い行の末尾に * を補って全体を N 行 M 列に揃える。次に配列を transpose して reverse する。この時点でほぼ出力ができあがってるんだけど、各行末尾の * を取り除いてから出力する。■C 問題「Balls and Bag Query」。Hash で個数を管理してサイズを答える。カウント 0 になった要素は取り除く。■D 問題「Cuboid Sum Query」。3次元の累積和。具体的にイメージができないので、機械的に操作をする。うまく1次元の操作を2次元3次元に拡張できるといいね。行列計算がそうだしこの問題もそうだったけど、自分は必ず演算対象の次元数を勘違いしてエラーを出す。数値に対して配列のメソッドを呼んだりする。包除も機械的にやる。3つの次元について L を使うか R を使うかの2通りがあるので全体では 2^3=8 通りの累積和を足し引きする必要があるが、3つとも R を選ぶのが全体であり、そこから引いたり引きすぎたものを足したりするのだから、3個の R を選んだ場合をまずプラスにして、つまり奇数個の R がプラスで偶数個の R がマイナスになる。これはコンテスト終了後に AC が出たからわかったようなことを書いている。コンテスト中は飛ばして E をやっていた。D と E がどちらもたいへんなら配点の高い方に時間を使う。■E 問題「Manhattan Multifocal Ellipse」。D (入力値) の最大値が 100 万だということで、平面上のエリアを具体的に特定できると思った。X 座標と Y 座標を分けて考えるけど、それぞれプラスマイナス D の範囲に限ることができる。D×D は通らないけど、1つの軸を1つずつ走査しながらもうひとつの軸が効率的に数えられたらいい。(WA×1/TLE×5/AC×23) までは行ったんだけど、log の2乗は「効率的」ではないんだよなあ。でも尺取りをするのもたいへん。(WA×1/TLE×11/AC×17) の構造を崩して改善した結果がさっきの TLE×5 なのであって、さらにぐちゃぐちゃ書き直したくない。それが済んでも WA×1 が残る。■F 問題「Maximum Composition」。制約が小さい。K は 10 以下だし、(A,B) の組み合わせも 2500 通りしかない。ということは最大 20 万個の (A,B) には重複がたくさんあるのだが、K 個以上は K 個と変わらない。という感じでなんとなく状態数が少ないような気がしたからといって、メモ化再帰をするだけでは TLE になるのだなあ。コンテスト終了後に取り組んだけど TLE だった。■なんと ABC の3完だ。自分のすべての提出。いまもって E も F も AC が出ないのだから、D の AC が時間内か終了後かはどうでもいいよ。どっちでもだめだよ。■E 問題。AC 出たよ。提出 #56580497 (AC / 942 Byte / 642 ms)。一方の軸を走査しながらもう一方の軸について数えるのではない。それぞれの軸について独立に、座標を、距離の総和に変換する。あとは距離の列と列をソートして尺取りっぽく和が D 以下になる組み合わせを数える。そうしたら TLE が解消しただけでなくなぜか WA も消えていた。コードはほぼすべて先の2つの提出のコピペであって、なんの修正もしてないのに、謎だ。これで A から E まで AC が出たけど、100 分で5完の目がある問題セットではなかったな、というのが感想。D も E も実装が重い。かといって4完ではなく3完なのは全くの想定外。これまで2回くらい脳みそにクモの巣が張ってるって日記に書いてるけど、それってブレインフォグっていうんじゃあありませんか?(何かのせいにしたいだけ。キミはもともとそんなのだ)■F 問題。どうするのかなあ。引数が大きくなればなるほど A が作用する比重が B のより大きくなっていくのを利用して状態数を減らしたい気がするんだけど、A 対 B の2次元の表に線を引いてトップ K を選ぶことってできない。■D 問題の提出一覧を見てると、自分のが特に遅い。3秒制限ぎりぎりだから当然なんだけど、どこが遅いのか。3次元累積和の作り方が下手っぽい。自分のは余分にループが回っている。なにか標準的な手順があるのだろうか。自分はまず3次元空間を用意して、3つの軸について順番に一方向に寄せるように累積和を作成していった。無駄があるのは、3つのパスで順番に、1次元累積和の作成、2次元累積和の作成(1次元累積和のマージ)、3次元累積和の作成(2次元累積和のマージ)を行っていて、たぶん3つともに3乗の時間と空間が使われているところだと思う。どうもね、すでに更新済みの累積和を参照しながら1パスで3次元累積和を構築してる提出が多いみたい。それってややこしすぎません?


2024年08月09日 (金) BABY IN CAR と書かれたステッカーがぺたぺたと車に貼られるようになって久しいが、今日前を走っていた車に貼られていたものには THE BABY IN CAR と書かれていた。それを見て、えっと、自分は知らないんだけど、どの(どんな有名な)ベイビーが乗っているんですか、という疑問がぐるぐるとわき上がってきた。そうするうちに文字の横にある模様がノースフェイスのものだと気がついた。つまり、THE NORTH FACE をもじった結果として THE BABY IN CAR と書かれているのであって、元ネタありきだから、文字単体で見ると不自然なものになってしまっているのだなと納得したのだけど、どうだろうか、同意してもらえるだろうか。おかしなことを言っているだろうか。


2024年08月03日 (土) [AtCoder] 今日はトヨタ自動車プログラミングコンテスト2024#8(AtCoder Beginner Contest 365)があった。解けるはずの D を落として ABCE の4完。F も G も歯が立たず。■A 問題「Leap Year」。どんどんネストを深くしていくこともできるし、ガード節を並べるみたいにもできる。leap? メソッドを使うこともできたみたいだけど、仕様の異同を確かめるのも無視するのも避けたい。■B 問題「Second Best」。max_by メソッドに引数を渡す。■C 問題「Transportation Expenses」。二分探索をして下さいと言われているように見えて、実は二分探索は必要ない。だけど二分探索をしないことでタイムが縮んだりしなかったのは、最初に A 数列をソートしていたからだった。無駄にややこしい解法を選んだかもしれない。■D 問題「AtCoder Janken 3」。最初の提出が WA だった。表か裏の2択だと思ったけどそこまで単純ではなかった。そこで無駄に考えすぎて SS.SPP.PRR.RSS... という順方向の並びと S+R+P+S+.. という逆方向の並びの連鎖を考えなければいけない気がして、こんなん ARC でしょと困惑していたのだけど、実は直前の手が何であったかという3状態の DP なのだった。AC は終了3分後。初手を間違えたあとのデバッグと方向転換が苦手すぎる。■E 問題「Xor Sigma Problem」。ビットごとに累積和を考えるだけなんだけど、0と1を反転させるだけの操作ができないんですよ。なかなかサンプルが合わなかった。■すべて 35 ℃を超えている室温が悪い。自分のすべての提出。■■■精進@翌日。F 問題「Takahashi on Grid」。位置の管理を BIT でやりたいとはコンテスト中に考えていた。でも L..U の範囲から外れた点をぞろぞろと移動させる方法が思いつかなかった。一日経ってもう一度考えたら重み付き UnionFind で集団大移動ができる。だいたいの点は L か U というエッジに寄せられて集団を作るので、代表点だけを動かして代表点にコストを計上する。各点のコストは代表点からの相対。エッジに寄らない点はコストがかからない点なので、ありがたく無視する。手段がわかればあとはやるだけ。std::map がない Ruby ではいくつもの道具を組み合わせてクエリの集団を Y 座標でソートした状態を保たなければいけないのが面倒で間違えやすい。■提出 #56361608 (AC / 2193 Byte / 2675 ms)。お風呂でエアコーディングを済ませていたのでばっちり2時間半(!)で AC ですよ。どうやってこれをコンテスト時間内に完成させられるんだ……。sx<tx を勝手に仮定していただとか、Y 座標の移動コストばかり気にしていて X 座標の移動コストを忘れていただとかの細かいバグはすぐに修正できたけど、一番難しかったバグが重み付き UnionFind の Find 関数にあったもので、1時間以上苦しんでいた。重みを更新するのにあたって条件が必要だとは思わなかった(58 行目)。このバグがあってもサンプル3の1つのクエリ以外は答えが合うのが怖い。サンプルが仕事をしていてたいへん助かりました。あとは BIT を使うにあたって座圧をさぼったら2つのケースで最長 221 ms 制限時間をオーバーして修正に 20 分。どうやってこれをコンテスト時間内に完成させられるんだ……(2回目)。■F 問題。Ruby での提出一覧を見たらもう AC を出している人がいた。提出 #56324651 (hmmnrst さん / 1304 ms)。自分の倍早い。中身を見たら GridRange と SegmentTree という、自分とは違う道具立て。セグメント木は BIT のスーパーセットだということで共通点はあるけど、GridRange ってなんぞ?■セグメント木を昨日考えなかったわけではない。一度 L か U に寄せられた点は以降決まったルートを辿るわけで、中間をすっとばしてゴール地点 X=tx における Y 座標が1つか2つ求められる道理。それができるのがたぶんセグメント木。ゴール地点において Y=ty との差分をコストに計上するのも簡単。でもスタート地点 X=sx における Y=sy が扱えなくて行き詰まった。sy は L でも U でもない。それをうまくやる方法があるということなのかな。セグメント木に何を乗せるかだよな。クエリに答えるのに都合のいい情報。わからん。


2024年07月27日 (土) [AtCoder] 今日は日本レジストリサービス(JPRS)プログラミングコンテスト2024#2(AtCoder Beginner Contest 364)があった。先週(20240720)と似たようなことを書いてもいいかな。Ruby にとっては D 問題が難しすぎて TLE が解消できなかった。D と F を見比べて、F の方に見込みがあると思って F 問題に8割方集中していたが時間内には解けなかった。終了 11 分後に F 問題の AC が出た。あとちょっとで解けたと思えばこそ得点にならなくてくやしい。■A 問題「Glutton Takahashi」。サンプルの2で示されているのは親切だけど、そこそこ殺意の高い罠がありますね。全部食べたあとで気持ち悪くなるパターンがある。sweet\nsweet を検索していたのを修正して sweet\nsweet\ns を検索するようにした。■B 問題「Grid Walk」。やります。B 問題にしては実装が重め。グリッドのサイズが小さくても実装量が減るわけではないんだよね、当たり前だけど。そこんとこ承知してくれているかな?■C 問題「Minimum Glutton」。C 問題で DP か? と一瞬身構えたけど、最小値を求めるということで、2通りの貪欲法を比較するだけ。大丈夫です、最大値を求める DP 問題は E にあります。■D 問題「K-th Nearest」。二分探索してくださいという問題にしか見えなくて他に方法が思いつかないんだけど、制限時間3秒のところ、(1割増しの 3.3 秒ではなく) 3.22 秒かかって TLE だったので、220 ms ほどの高速化が必要。どうするの?■E 問題「Maximum Glutton」。C 問題の難しい版。甘さとしょっぱさの組み合わせを状態のキーにはできないけど、甘さとしょっぱさのどちらかと個数を組み合わせてキーにすることはできる。甘さをキーの1つにしたら、しょっぱさを最小化する DP をする。これもサンプルが教えてくれたんだけど、A 問題と同じ罠があります。同じ罠に落ちかけました。■F 問題「Range Connect MST」。どういう風に辺を引くことになるのか、イメージがしづらい。木なので本数は N+Q-1 本だと決まっている。それを最大 N×Q の組み合わせからどう選ぶと全域木になるのか。あれこれ考えてようやく納得できたのは、i=1..Q において、Li..Ri のあいだに連結成分が g 個あるなら、g 本の辺を引くのだということ。両手の 5+5 本の指を使って考えると、それで N+Q-1 本の辺が選ばれるようだったのでそう思った。答えが合わなくて時間内に提出できなかったんだけど、原因がしょうもなくて、貼り付けた BIT のイニシャライザにある初期化コードが今回は不要だと思って削除したけど、削除してはいけなかったという、そういう理由で答えが合わなかった。たとえばヒープだと、ソート済みの配列を内部データにする場合、初期化の必要がない。ソート列はそのままでヒープの要件を満たしている。だけど BIT の内部データは違うんだなあ。解ける問題だったなあ。1から数年前の自分なら解いていたなあ。■自分のすべての提出。最近ユーザー名の横に表示されるへの字。ノイズではあるんだけど、水色でもまだ 1500 台を維持しているなという慰めにもなっているもよう。■精進。D 問題。Q のループの中で二分探索をする中で二分探索を2回行って TLE を出していた。二分探索の上限を指定せずに TLE×11。上限を指定して TLE×7。最も内側にある2個目の二分探索を省けるときは省くようにして TLE×1。最内の2個目の二分探索を完全に省いて AC。これが 1765 ms なんだけど、Ruby で 627 ms で解いている人がいるんだよね。気になるけどネタバレは嫌だ。■D 問題。別解。提出 #56088391 (AC / 325 Byte / 340 ms)。log 1つでできると読んだので、k 幅のウィンドウを二分探索で置いてみた。判定条件は、右端の要素が初めて左端の要素よりも b から遠くなる瞬間。そのひとつ手前では逆に、左端の要素が右端の要素より b から遠くなっている。この両者を比較する。二分探索の高速化っていうと尺取りが定番なんだけど、だから昨日はその方面で TLE 回避策を考えたりしてたんだけど、この D 問題はウィンドウの幅 k がクエリごとに可変だから、尺取りはうまくない。今日の提出では同じ二分探索を使っていてあまり違いを感じないんだけど、よく見れば二重の log が一重に減っている。log 1つの差ってたしかにこれくらい微妙なものではあった。Pairs を解いていたときに書いている。「log ひとつの差ってちょっとした違いなんですよ。ちょっと見る角度を変えるだけ」。提出したあとでもういちどさっき読んだブログを読み返すと、まんま自分の提出がやっていることが書いてある。「初めて左端のが遠くなった場所を見つけてその一つ前も(存在すれば)候補なので比較して」。なんかよくわからんなあと思いながら読んでいたけど、実際今も「自分より1離れたもの同士を比較」「長さが1短い区間を考えて」「初めて右のが遠くなったら左のを付け加えてそれがそのまま答え」とかよくわからないんだけど、必要なことは一度読んだだけで頭の中に入ってるんだな! 自分では思い出せないだけで。


2024年07月20日 (土) [AtCoder] 今日は AtCoder Beginner Contest 363 があった。Ruby にとっては C 問題が難しすぎて解けなかったが AC を出している人もいる。精進が足りない。Ruby で通している人が1人でもいるなら自分も通せなければいけない。なんなら自分が最初で唯一の AC でありたい。E まで解いてから F と C を見比べて F の方に見込みがあると思って F に専念していた。その見込みは間違っていなかったが、TLE を出したあと誤った解法の実装にのめり込んでいる内に時間切れになった。実は関数に引数を1つ付け加えるだけで AC になったのだが、30 分ちかく時間を残していてそれに気がつけなかった。くやちい。■A 問題「Piling Up」。全パターンの差分を並べてもっともふさわしいものを選んだ。短い解答を見てみると、すべての基準値が 100 の倍数だということを利用していた。そういう法則がすぐに見抜けないし、見抜けるまで問題を読んでもいない。■B 問題「Japanese Cursed Doll」。ソートして P 番目なんだけど、昇順ソートして前から P 番目ではない。それを間違えていてもサンプル3を試すまでは答えが合うのが怖い。■C 問題「Avoid K Palindrome 2」。順列全列挙では間に合わなかった。どうやるの? C 問題で順列全列挙以外はやる気がないよ。■D 問題「Palindromic Number」。これぞ ABC の D 問題、緑 diff って感じの問題。実際の難易度が何色かはまだ知らないけども。順番に生成して 10^{18} 番目まで数えることはできないので、まずは桁数を決めて、桁数が決まったら先頭の桁から確定していった。■E 問題「Sinking Land」。UnionFind です。最大6桁の数字が 100 万個という入力はそれだけでけっこう重い処理。正確さを重視して富豪的に書くと AC と TLE のボーダーを超える可能性があるので、全体を通して最初から書き方を選んでいく……余裕があるといいね! グリッドの各マス+海用に1個の頂点を用意して、海抜の低い順に、自分より低い頂点とのあいだで結合する。海のマスを数えたら陸のマスが求まる。■F 問題「Palindromic Expression」。やるだけの問題だと思うんですよ。解けたはずだと思えばこそ悔しい。まず中心に N を置きます。これが最初から条件を満たしているならこれが答え。さもなければ、N を L*M*R で置き換える。L と R は左右対称でなければならず、M に対しては再帰的に同じことを繰り返す。2数の積で N を割っていくので、N≦10^{12} だから、L,R として列挙する数は 10^6 個程度に収まって許されそう。終了条件を N<[L,R].min**2 として愚直にやったら TLE だった。N や M に関して結果をメモしたら数は減ったけどまだ TLE だった。AC になった鍵は、A*B*C*D*E という状況で A<=B でなければならないと定めたこと。こういう方針自体は TLE が出た直後に考えて実装に取りかかってるんだけど、なぜか実装の途中で L が回文数でなければいけないという条件が加わっていて、その実装に忙殺されてしまっていた。なんでだよー。■自分のすべての提出。■F 問題。日記を読み返すと「なんでだよー」の答えが見つかるんだよなあ。L*M*R の L と R に関して、「L と R は左右対称でなければならず」と表現しているところに誤りの種がある。左右対称なのは L*M*R 全体なのであって、L と R について書くなら表現を改めて、「L と R は反転して一致しなければならず」とする方がよりふさわしいと思う。雰囲気で理解して細部がぼんやりだから途中ですりかわるんだよ。コンテキストが遠ざかったときに左右対称という断片に引っぱられて勘違いをする。■精進。C 問題。Array#permutation で N の階乗通りを列挙する代わりに、再帰関数で同じ文字を区別しないで列挙するようにしてもまだ TLE だった(#55827067)。X で読んだ通りに、1度しか出現しない文字を統合する前処理を施すことでさらに列挙数を減らすことができて AC になった。提出 #55827317 (AC / 536 Byte / 584 ms)。300 点問題にかけていいコストではない。同じ時間で D と E と F を解いたあとで初めてペイする。■■■D 問題。「N=19999のとき、正解が99999999(8桁)だけどコードが出す解が9999999(7桁)になっちゃうみたいですが通りはしました」と投稿している人がいて、試しに自分の AC 提出(#55797000)を実行してみたら 100000001 (9桁) になって、どちらでもなかった。投稿主の提出(#55831310)をコードテストで実行してみたら、たしかに書いてある通り7桁の9が出力された。正しい答えはなんなんです? Ruby の AC 提出2、3個で多数決をとったら、正しい答えは 99999999 (8桁) らしい。入力と出力の見た目には桁数が違っても維持されるパターンが見られるのだけど、自分の提出は N=100000 型とか N=999999 型では他と一致していても、N=19999 みたいな型の入力に対してはずれている。だって N=199 と N=200 の答えが同じなんだもん。サンプルの3を合わせるために ≦ を < に修正してから提出したのだけど、たぶん修正すべき場所が間違っていた。ザルなジャッジにお目こぼしされてしまったか。提出 #55833437 (AC)。after_contest はまだないみたいだけど修正版を提出。N=19...9 と N=20...0 っていうのは、出力の桁数が変化するという点でのコーナーケースみたい。テストケースにコーナーケースがなかったんだね。■E 問題。別解。提出 #55860799 (AC / 523 Byte / 707 ms)。UnionFind 解が #55800705 (AC / 524 Byte / 1629 ms) だったので、長さはほぼ同じなものの、時間は倍以上早くなった。別解は BFS。プライオリティキューすら必要ではなかった。■精進。「砂の城 (Sandcastle)」。E 問題と似た問題だと名前が挙がっていたので。提出 #55862476 (AC / 354 Byte / 578 ms)。たしかに、これも BFS だった。かなりシンプルに書けたと思う。


2024年07月17日 (水) 二三日は何日なのか問題。もちろんこれが問題になったことはない。自分としては、四六時中や四十九日、七十五日、三々五々から演繹するとニサンニチになるしかないと思うのだけど、実際にそうだったことがあるのを知っていることからもそう考えるのだけど、今 ATOK で明鏡国語辞典を引くと「しじゅうく‐にち【四十九日】〘名〙 人の死後四九日目に当たる日。また、その日に行う法要。七七日(しちしちにち)」とか書いてあって、これもうわかんねえな。いやホントはわかりますよ。「四十九日」「七七日」の2つと「四九日(目)」とでは使用時期にずれがある。辞典の言葉は収録される言葉よりも新しい。23 日を二三日と書くことの帰結として、10 月が一〇月になっていたのが先々週読んでいた小説。何を今更感しかないけども、そうなんだ、そりゃそうなるよな、だけど気持ち悪いな、その○はなんなんだ、漢字ではないよな、とひっかかってしまったのが今日の日記になっている。十月って書きたいよ。■ATOK で 10 を変換して出した一〇で使われているのは○ではなかった。U+3007 は「漢数字ゼロ IDEOGRAPHIC NUMBER ZERO」であり、U+25CB 「丸印,白丸 WHITE CIRCLE」とは異なる文字だった。〇が比較すると新しい字だけど数百年はさかのぼれるみたいなことが Wikipedia の漢数字のページに書いてある。お前漢数字なのか。漢数字とアラビア数字を区別して一方では位取り記数法を認めたくないっていうのは、単に好みや慣れや、旧弊に囚われているってだけなのか。自分はこれからも区別して用途を分けていきます。■用途を分ける(漢数字では位取り記数法を使わない)ことの別の一面は、「健康第1」とか「3位1体」とか「別の1面」とは書かないということ。算用数字を使うのは1、2、3と数を数える場面だけでいい。X と X+1 が可換な場合だけでいい。


2024年07月16日 (火) 子供の頃にスーパーボールで遊んだ記憶。今でも理解できないし説明できないのだけど、たとえば床に2バウンドさせて壁にぶつけるとする。1バウンドした位置から A の距離で2バウンドし、そこからさらに B の距離で壁にぶつかるとする。この A と B の距離(あるいは時間)の比率が跳ね返ったボールの跳ね方に反映されて、不等間隔の規則的な跳ね方を繰り返す。だったよね? おもちゃにも流行り廃りと技術や素材のアップデートがあって、今の子供はスーパーボールなんかでは遊ばないかなあと思ったので書いた。


2024年07月13日 (土) [AtCoder] 今日はトヨタ自動車プログラミングコンテスト2024#7(AtCoder Beginner Contest 362)があった。特に書くことはないけども参加記録として。自分のすべての提出。■A 問題「Buy a Pen」。3通りなのでどうとでも書きます。■B 問題「Right Triangle」。2乗のまま整数のまま三平方の定理で判定します。■C 問題「Sum = 0」。最初はすべて L に寄せて和の最低値を求めます。あとは和が負である限り、各組 R-L を上限として右に寄せていきます。■D 問題「Shortest Path 3」。辺だけでなく頂点にも重みがあって何かが変わるかなと考えたけど、普通に普通のダイクストラ法のままだった。■E 問題「Count Arithmetic Subsequences」。N の制約が 80 以下と非常に小さい。これだけ小さいと O(N) と O(logN) の差よりも定数倍の違いの方が大きくなりがち。80 の3乗が大した大きさでないことを確認して愚直3重ループを書いたら TLE が出てびっくりした。3重ループと書いたけど実は3つ目のループは再帰関数の中に隠れている。それって3重にとどまらないのでは? やばいのは公差が0のケースなのでそれだけ別で数えて AC。■F 問題「Perfect Matching on a Tree」。マッチングとかフローとか辺被覆とか点被覆とかはわかりません。「最大重み最大マッチング」てなんだったんでしょうか。まんまと「知らないキーワード、怖い」と G 問題に流れていっちゃったけど、この問題は難しいことは考えずに、木の中心で頂点集合を2つに割って組み合わせたらどうかなと思った。WA だった。この問題の何が問題なのかがわかっていないか、うまくできなかったか、どちらなのかもわかりません。とはいえ、うまくできていなかったのは間違いない。中心に頂点があるとして、中心から3本以上の辺が出ている場合が考慮できていなかった。■G 問題「Count Substring Query」。ミシシッピの s が足りないのが気になってしょうがない。なんでコンテスト中に辞書を引いてるんだ……。F 問題より見込みがあるかと先に考えていたけど、数を数えようとするとどうしても2乗の時間がかかりそうになる。接尾辞配列を使うだけみたいなツイート(?)を読んだ。その使うだけもできないのだから惜しくも悔しくもないけども、接尾辞配列とか完備辞書とか Trie とか、見て見ぬ振りをして実装せずに来たツケなのは間違いない。「SA-IS 法のメモ - まめめも」8割くらい読んだ。しれっと挟まれた翻訳者自らの宣伝だけど、その本はもう持っている。悲しいかな理解が追いつかなくて積まれているけども。SA-IS 法に話を戻すと、2割が完遂できなくて写経することしかできない。たとえ写経でもするべきなのかなあ(やらないつもり)。やると書いて、やる、有言実行メソッドと、やらないと書いて、やる、天邪鬼メソッドの両方があります。自分のモチベーションをより高める方を選んでどうぞ。■■■精進。F 問題。提出 #55644781 (AC / 1365 Byte / 636 ms)。やりたいことのイメージは合ってたみたい。中心で2つかそれ以上の頂点集合に分けて、異なる集合から1つずつ選んでペアを作る。それをどうやってうまくやるか。葉から処理を始めて、親にどんどん頂点集合を集約していく。その集合の大きさが過半数になってはいけない。最終状態がいくつか考えられる。わかりやすいのが、集約された結果ある頂点とその2つの子が残り、そのサイズとつながりが (N/2)-(1)-(N/2) である場合。左右の子から1つずつ頂点を取り出してペアを作ればいい。総頂点数の偶奇により中央に頂点がない (N/2)-(N/2) のパターンも考えられるがやることは同じ。もうちょっとわかりにくいのは、ある頂点が3つ以上の子を持っていて、どの子に軸足を移そうとしてもその瞬間に他の子が過半数の大きさの頂点集合を作ってしまう場合。最も単純なケースは、2から N のすべての頂点が頂点1につながるスター型のグラフ。頂点1を中央に据えて、他のどの子の頂点集合もサイズはたったの1だけど、どの2頂点もマージできない。そんな感じで頂点集合をマージできるだけマージして残った頂点集合の集合からペアを作るのが次のステップ。同じ集合の中からペアを選びさえしなければいい。だからプライオリティキューを使ってサイズの大きい頂点集合から優先して2つ選んでペアを作ることを繰り返した。もっと洗練されたうまいやり方と考え方が絶対にありそうだけど、AC なんやしまあええやろ(向上心のない怠惰な態度)。あと、自分が中心と呼んでいるものに重心という名前がついているらしいのは何か所かで読んで把握している。■■■F 問題。某週記を読みまして DFS 順でペアが作れると知った。DFS の行きがけ順(に限らない?)に 27 個ないし 26 個の頂点を A-M$N-Z と並べたなら、(A,N),(B,O),(C,P),...,(M,Z) のペアを作る(のだと思う)。ローカリティに注目すればなんとなくそれでいいような気がするし、逆に両端からペアを作っていくのがダメなのもわかる気がするが、いつでも絶対それで OK とはわからない。■自分の解答の後半のステップはプライオリティキューを使わないで、単純に入れ子配列をフラットにして真ん中で切ってペアを作るので良さそう(C.flatten!; C[C.size/2..].zip(C))。どの解説もそういうペアの作り方をしてるので。どの頂点集合も過半数に届かないようにしているのだから、たしかにそれで同一集合からペアを作ることはないみたい。それと、DFS での頂点の並べ方は帰りがけ順でも OK と解説に書いてあった。