/ 最近 .rdf 追記 設定 本棚

脳log[2023-08-02~]



2023年08月02日 (水) [AtCoder] 精進。ARC010-C「積み上げパズル」(黄 diff)。色ボーナスもコンボボーナスも全色ボーナスもマイナスになりうるのがゲームのルールとして直感に反する。でも DP をするならそこは問題にならない。DP 配列を埋める初期値にだけ注意したらあとの遷移では比較して大きい方をとるだけ。どういう DP をするか。コンボボーナスを加算するために最後に積んだ色が何色かで M 通りに分類する。全色ボーナスを加算するためにこれまでにどの色を使ったかを 2^M 通りのビットフラグで分類する。M の上限が 10 なので組み合わせた状態数が最大で 10240 通りになる。遷移の回数 N が 5000 以下だからおおよそ 5000 万の処理量は Ruby には厳しいかと思ったけど、テストケースが制約の上限を攻めていないのか処理時間には余裕があった。定数倍2分の1のために必ず立っている1ビットを効率的に省略する方法を考えていたけど必要なかった。■WA×41WA×18WA×3 のち AC。■41→18 で解消したバグは、色ボーナスとして加算する値が落ちてきたブロックではなくすでに積まれている一番上のブロックの色によって決まっていたうっかりミス(21 行目)。■18→3 で解消したバグは、ブロックが1つも積まれていない状態(スコア0)が存在していなかったこと。■3→AC で解消したバグは、それまではちゃんと対策できていたのに他のバグを解消したときに不要になった気がして省略した処理がやっぱり必要だったために生じた新しいバグで、最初のブロックの扱いをどうするかに関わること。色数 M が1より大きいなら特別扱いは必要なくて、他の色の空の状態(スコア0)から遷移してくることでコンボボーナスなしの色ボーナスだけを加算することができる。しかし使われている色が1色だけのときに特別扱いなしでは最初のブロック(コンボボーナスなしの色ボーナスだけ)がうまく扱えていなかった。M+1 色目が追加できたらややこしいことは何もなかったけど、TLE が怖くて1ビットだって削りたかったときにそれは取れない選択肢。


2023年07月29日 (土) [AtCoder] 今日はユニークビジョンプログラミングコンテスト2023 夏 (AtCoder Beginner Contest 312)があった。以下 ABCDF のふりかえりと GE の精進を。■A 問題「Chord」。こういうのはコピペを活用してできるだけミスの混入を避けるべきなんだよね。そういう意味で自分の提出 #44032329 はコピペしたあとに手を動かしていて良くない。Shiro_S さんの提出 #44031960 を見習いたい。■B 問題「TaK Code」。すっかり実装が面倒くさい枠の B 問題。4×4×2=32 マスの黒白を愚直に確かめるだけ。そんなんでも添字の範囲を間違えたりしてたいへんなんですよ。■C 問題「Invisible Hand」。ぼんやりして全然頭がはたらかなかった(いつものことか?)。問題文を読むかぎり必ず答えが見つかるみたいな雰囲気を感じるけど、そうだとはわからない。目に付いた数字で二分探索をするもサンプルの2がありがたくも優しくそれではダメだと教えてくれる。A、B 数列の両端付近の値を候補に加えて二分探索で答えを探したけど WA だった。それならと各数字の前後を候補に加えて 3N+3M 個の数字から答えを探したら AC だったけど、あまりにひどい力業。TLE になっていたらお手上げだった。Ruby で2番目に早く提出された gmmtea さんの決定的な提出 #44046237 がすごい。探索などしていない。■D 問題「Count Bracket Sequences」。2乗が通る制約。(閉じていない)開き括弧の数ごとに場合の数を記録する DP をやってみたら DP 配列を shift したり unshift したりする面白い形の遷移になった。もっと効率的なやり方(※)がありそうではあるけど、制約で許されているので愚直に配列を操作した。※倍の長さの配列を用意して真ん中を暫定の先頭にする。実は Ruby のインタプリタが shift も unshift も勝手にうまくやってくれるので考える必要がない。■E 問題「Tangency of Cuboids」。幾何問題。心中リスクが高いので飛ばした。終了3分後の提出 #44081331 は TLE だった。■F 問題「Cans and Openers」。配点は E 問題と同じ。貪欲法でできるかなと考えてみたけど、缶切りの数に応じて答えがガタガタして無理そうだった。場合分けも、缶切り不要の缶が M 個に足りない場合や、缶切りがあっても缶切りが必要な缶が十分にない場合など、状況と状況の重ね合わせまで考えるとたいへん。2本の累積和からいい感じに M 個を取るという構図から ABC116-D Various Sushi を思い出していた。しかしその精進(20220609)は生きず。BIT を2本使って缶切りの数ごとに愚直に満足度の総和を求めた。Ruby によるすべての提出を見ると、貪欲っぽいもの、プライオリティキューを使うもの、DP らしいものなどいろいろ。log が付くデータ構造を使った自分の提出 #44067838 などは時間が余分にかかっていて、もうちょっと頭を使ってがんばりましょう、という感じ。■G 問題「Avoid Straight Line」。コンテスト終了後に本腰を入れて考えた。単純パスを構成しない3つ組とはどういうものだろうか。制約を見るとペアを考えることが許されていないので、ペアから LCA を求めて……という解法にはならないとわかる。それで、どういう3つ組? 根っこに向かって一直線の3つ組ではない。2頂点とその LCA から成る3つ組(ペアの場合もあるが無視する)ではない。根っこに向かう直線ペアと×××の組み合わせではない。……。部分木ごとに直線ペアの数と分岐するペアの数を記録して根っこに向かう DP をすれば求めたいもの(分岐するトリオの数。変数名 spt は split trio の略らしいです)が計算できそう。提出 #44086692 (AC / 488 Byte / 1741 ms)。サンプルの3が合わないときは3つの子から1つずつ選ぶ spt を数え忘れてるかもね(経験談)。Ruby による G 問題へのすべての提出を見ると自分の遅さが際立っている。なぜ?■コンテスト成績証自分のすべての提出。ABCDF の5完でまずまずの成績。(終了後でも) G 問題が解けて満足している。AtCoder Problems によると難易度勾配は F(水)<G(青)<E(黄) らしいけどね。たしかに、E 問題の TLE を解消する手立てがまだわからない。直方体の数が 10 万あるのに、あれとこれが接触している、これとそれが接触している、と個別にペアを数えてはいけないのかもしれない。だけど6面あって重複をカウントしないように何ができる?■■■G 問題。自分の提出が他と比べて桁違いに遅いのでちょっと考えてみた。前回は子(部分木)ごとにサイズと直線ペアの数を記録して求めるもの(分岐トリオ)を計算して足し合わせていた。分岐トリオを求める計算が余事象が関わっていてちょっと面倒くさい。今日は子(部分木)ごとにサイズと直線ペアと直線トリオを記録して、根っこで一括して余事象を求めるようにした(3つ組の選び方から直線トリオを引く)。提出 #44113272 (AC / 309 Byte / 602 ms)。桁は並んだけど 400 ms 台、500 ms 台に比べると遅い。メモリ消費が4倍くらいあるんだよね。根っこから再帰関数を呼び出すのでなく、葉から根へ積み上げるべきなのかもしれない。だけど記述のシンプルさにはあらがえない。簡潔に書けてると思うん。ゴルフしてるものにくらべるとまだ余計なことをしてる無駄な記述があるみたいだけど。■E 問題。座標空間が 100×100×100 でえらく狭いなと、ここに直に書き込んで何かできないかと考えてはいた。でも 100×100×100×N では TLE だしなーと。見落としていた重要な制約があった!!! 「直方体同士は体積が正の共通部分を持たない」 だったら N 個分書き込んでも総和は 100 万以下じゃん! 隣のマスを見たら隣接直方体がわかるやん! 提出 #44116267 (AC / 553 Byte / 729 ms)。■延長1日を含めてこれもう実質7完でいいんじゃね? やったぜ!(めでたい)■■■G 問題。現在見ている頂点を3つ組の真ん中の頂点だと考えて直線トリオを数える DP ができるらしい(最後に3つ組の全選び方から引く)。提出 #44139844 (AC / 303 Byte / 634 ms)。そうすると直線ペアの数を数える必要がなくなって、サイズと直線トリオの数だけを数えればよくなったけど、期待に反してわずかに遅くなった。400 ms、500 ms 台に入ると思ったのになぜ? 実は答えを合わせるのにすこし苦労して、現在見ている頂点を3つ組の真ん中に決め打つということは、他の2頂点を選んでくるのは子方向の部分木だけに限らず親方向からも選んでこられる(16 行目)。その点がこれまでの2つの解答と異なっていた(これまでは現在見ている頂点が LCA になるような3つ組を考えていたので問題が子孫方向の部分木に閉じていた)。■E 問題。たぶんだけど、サンプルにわかりやすーい図解が付随していたら当日の AC がもっと多かったと思う。問題文を読んでも全然具体的イメージが構築できなくて苦労したもんね。まず直方体の対角線って何だ、とか。それは長方形の対角線とは(必ず)違うと考えていいのか、と。不等号(X1<X2, Y1<Y2, Z1<Z2)に意味を持たせすぎないでもっと文章で説明してくれていいのよ。辺が軸に平行だということも、え、それってどういう、という疑問からなかなか先に進めなかった。脳内直方体は歪んでいた(それは直方体ではなくない? 菱餅? そんなつっこみも出てきやしなかった)。■■■F 問題。自分の提出は BIT を使ったせいで余分な log がついて時間がかかっていた。よく考えて整理した結果、2本の累積和から M 個を取ったあとで綱引きをするようにした。要するに尺取り。提出 #44215202 (AC / 619 Byte / 389 ms)。最速級まではいかないけど BIT 版より倍は早くなった。じっくり整理したので規則的で理解しやすいと思う。S2 に前加工を施すと k1x の補正が必要なくなるし、無意味に缶切りを取得する前に試行を中断できるけど、それは TODO。


2023年07月28日 (金) 初めて免許を取ったときの教習の一幕。S 字を通過していたら止められて、助手席側のドアを開けてみせて見てみろと。こんなにタイヤと縁石が近いと。はあ、なるほど。で、どうすると問われました。(ぎりぎりだけど当たってないので)行けるんなら行きます、と前進しようとしたら呆れられました。いやあ、だって、ねえ、ドアを開けて目視でセーフが確認できたんだから、行きますよねえ。熱烈指導(お説教)が始まりそうな気配だったので慌てて(ルームミラーと後方を確認して)バックして進路を修正しましたけども。前進しかできないわけではないことを証明しましたけども。■何の話かというと、「(ぎりぎりでも、忠告を無視するような形になっても)行けるんなら行きます」という選択をする傾向が他の場面でもあるかもなあと思ったからなのでした。


2023年07月26日 (水) 動悸という言葉について。辞書には「心臓の鼓動が平常より烈しいこと。また、その鼓動」「動悸がする」「動悸を打つ」などと書かれている。つまり動悸という言葉の中にすでに鼓動の早さ強さが含まれている。今日「動悸が早くなった」という用例を本で見かけた。珍しくはない使われ方だけどちょっとひっかかる。「鼓動が早くなった」とか、ちょっと古めかしい定型かもしれないけど「心臓が早鐘を打った」とかで良くない? でも、すでに動悸がある状態がさらに程度を増したというように読み取ることができなくはないかな、と思いもする。そのつもりで書かれただろうとは思わないけど。辞書に書かれているのは前例であり、現在の用例も前例のひとつになりうる。動悸という言葉が使いやすい形と意味を持っていないバグが是正されていく過程にも思える。どっちつかずの微妙なきもちにさせられる言葉。あるいは個人的な見当外れのこだわり。「HTTP プロトコル」の仲間に見えるんだよなあ。Horyuji Temple が仲間に加わるとむしろありの例なんだよなあ。


2023年07月25日 (火) [AtCoder] 精進1。ABC104-D「We Love ABC」(青 diff)。3つの要素があるときに真ん中に注目するという典型をこの前仕入れたのでその方向で考えてみた。各文字を見ていくとき、その文字が B である場合と、? を B にする場合に ABC 数を数えて足し合わせたい。前準備として左にある A の数、? の数、右にある C の数、? の数を知るために累積和を用意する。既存の A のうちのひとつを利用する場合には左にある ? に ABC のどれを割り当てても良い。? のうちのひとつを利用する場合には割り当て可能な ? の数が1つ減る。わかりにくかったのは、文字列の種類数と ABC 数としてカウントするための A の文字の選び方の区別。つまり、? への文字の割り当て方は 3.pow(? の数) 通りあって、それぞれの文字列に対して A の数だけ ABC 数を数えるための重みがあるということ。同一文字列を重複して数えてはいけない気がして ? への文字の割り当て方に制限を加えて順序よく数え上げようとしていたのだけど、全然いらない心配だった。ある A(もしくは A を割り当てた ?)に注目して ABC 数をカウントするときと別の A(もしくは ?)に注目して ABC 数をカウントするときに、それぞれに S 全体が同一の文字列になるものがあってもよい。■提出 #43942246 (AC / 451 Byte / 220 ms)。■■■精進2。ARC092-D「Two Sequences」(黄 diff)。XOR を考えるときに問題を桁ごとに分けるのは基本だけど、足し算がかかわると繰り上がりがやっかいだよね。ひとつの対処法を知っている。ARC158-C「All Pair Digit Sums」を解いていたときのこと。「kotatsugame さんの動画を見ていたら k 桁目を見ているときにその桁の数字と同時に 10**(k-1) で割った余りを持てばいいと教えてもらった。たとえば A={1234,9876} だとして 10^2 の位に注目しているとき、(2,34),(8,76) を処理対象にする。余りの部分は小数点以下の数字みたいなもの。言われたら、たしかにそれでできそうではあるけど、でも、そういう発想はたぶん待っていても出てこなかっただろうな」。そうすると数列をマスクしてソートして、注目しているビットが1になる境界、繰り上がって0に戻る境界、さらに繰り上がってきて1になる境界がこの順で見つかる。時間が厳しいので二分探索を使わずに尺取りでやる。これは2か月に渡って苦しめられた Pairs の教訓。■提出 #43950013 (TLE×4) のち 提出 #43950581 (AC / 515 Byte / 2106 ms)。正直初手で通ってほしかった。TLE を回避した手段はイテレータメソッドを while ループに書き換え、Array#map と Array#sort を破壊的メソッドに置き換えたこと。読みにくくなるだけでアルゴリズム的な改善はない。■Corvvs さんの提出 #3092072 を見ると添字を3つも管理していない。キャリーにだけ注目している。AB 変数の意味もわからない。むむむ。■1のビットの数の増分だけ見てるのかな。キャリーが起こると1が2個減って上の桁で1が1個増える。2個減るのは XOR を考える上で意味を持たないけど、1個増えるのは答えに影響する、みたいな? AB 変数がベースラインで、rmask 変数がキャリーによる変動、とか? rmask 変数の末尾に無条件で 0 の桁を追加してるのもそれっぽい。自分でコードにできるほどはっきりはわかんないけども。■ところで、自分が他人のコードを読むときって、完全に雰囲気で読んでいる。個別の式の意味を解釈するよりも、全体の形(ループとか)に注目している。雰囲気だから「みたいな?」みたいな感想になる。たとえばこのときも「面白いことを書いている人がいる。「ABC303-D この問題の正解者は実は間違っていた」。形式だけ見て気が付いたことを」。どういう値を触っているかだけ見てその値の意味を理解しないまま感想を書いている。細かい話は苦手なんだよ。確率・期待値・数え上げ問題の数合わせとかもー最悪。■提出 #43953782 (AC / 484 Byte / 1605 ms)。キャリーによる増分に注目するので間違いなかった。でも伏兵があった。ベースラインとなる繰り上がりを考慮しない和(それは XOR と等しい)の XOR を求めるところに罠があった。項数 N が偶数の時はきれいに打ち消し合って0になるのだね。これはきっちり式を整理して計算しないと気付けない。■提出 #43989881 (AC / 842 Byte / 1496 ms)。Array#sort(一般に NlogN)の隣で二分探索を尺取りにしてもそれは定数倍の改善だし、3つの尺取りが1つの尺取りになってもやっぱり定数倍の改善だけど、ソートを工夫するところまでやると log が落ちて Nlog(N)log(max A) が Nlog(max A) になるっぽい。「数列のソートに桁ごとのバケットソートを使用します。下位の桁から順に計算していけば基数ソートの動きそのままになるので、各桁で O(N) でソートが可能です」 ちょっとだけ早くなった。ちなみに、配列の確保&使い捨て(15-16 行目)を抑制しようとして添字をちまちま操作しても Ruby の場合は逆に遅くなったりする。実際にローカルでは遅くなった。


2023年07月23日 (日) [AtCoder] 精進。ABC142-F「Pure」(青 diff)。すんごく難しかった、というか、ややこしかった。これってうまい書き方があるんだろうか。「すべての頂点の入次数が 1、出次数が 1 であるような G の誘導部分グラフ」というのは要するに輪っかのことで、しかも輪っかを横切るショートカット辺が存在しないもののこと。N の2乗が通る制約ではあるけども、すべての頂点を始点にして輪っかを検出して、輪っかのパスを辿ってショートカット辺が存在しないことを確かめて許されるのかどうか。■提出 #43905266 (AC / 967 Byte / 77 ms)。再帰関数のパラメータとしてパスを表す配列と、再訪確認のためにパスをメンバとするハッシュ表と、調べたけどダメだったことをメモするハッシュ表を渡している。もっとうまいやりようがありそうなものだ。■輪っかの検出ロジックは、終点を決めて、終点の隣接頂点の1つを始点にして終点への到達可否を調べる。そのときに始点や中継点において、終点に到達したパスの中に2つ以上の隣接頂点(←始点や中継点にとっての)が含まれていないことを確認している。2つ以上の隣接頂点が関わる輪っかはショートカット辺がある輪っかなので。これを愚直に繰り返すと TLE になったので、ダメだった結果をメモしている。輪っかにならなかったケースとショートカット辺があったケース。メモの利用は中途半端で、輪っかの終点を切り替えるごとにリセットしてしまっている。輪っかなんだから(輪っかを検出するための便宜上の)終点をどこに取ったとしてもショートカット辺があるせいでダメだったという結果は共有できるはずなんだけどしていない。あと必要だったのは終点を含まないところで輪っかができてしまうケースの除外。それはいま関心がある部分ではないので後の処理を待ってほしい。再帰関数で同一頂点への再入を検知してリターンしないとスタックを使い尽くしてしまった。■これって強連結成分を定型的に列挙してショートカット辺をちょちょいと調べて終わりだったりしたのでは? 自分は何か輪っかの検出で苦労して、輪っかの検出とショートカット辺の確認を同時にやろうとしてさらに苦しくなっている雰囲気。DFS 2回の SCC 分解がそらで書けるほど身についてないから仕方ないんだけど。■日記を書くために自分の解法を書いていたらこれがなんで AC なのかわからなくなってきた。それでひとつの発見があった。最初に「要するに輪っかのことで、しかも輪っかを横切るショートカット辺が存在しないもののこと」と書いたけど、ショートカット辺はただのショートカット辺ではなくて長さ1のショートカット辺だった。誘導部分グラフに含まれる辺と含まれない辺を考えれば当然のことだけど。そうするとひとつの疑問がわいて、どんなループでも、許されない長さ1のショートカット辺を本式の、ループを構成する辺として採用することで答えにすることができるのかどうか。できそうな気がする。それで実装が簡単になるかと思って別解を書き始めたけど、べつにそんなことはなかった。■提出 #43935918 (AC / 1048 Byte / 69 ms)。長くはなったけどややこしさは減って、シンプルに閉路検出&コンパクト化で答えが得られるようになった。■この問題を選んだきっかけはこの前の ABC311-C「Find it!」で閉路検出を書いたからで(#43848448)、その目論見が外れてやけにややこしい解法に当初はなったけど、別解ができてみると狙いが外れていなかったことがわかる。グラフの形がちょっと複雑になって、閉路のコンパクト化という処理を追加したものがこの Pure という問題だった。


2023年07月21日 (金) [AtCoder] 精進。東京海上日動プログラミングコンテスト2023(AtCoder Beginner Contest 299)-G「Minimum Permutation」(黄 diff)。愚直解法が作れたら、あとはセグメント木を知っていますかというだけの問題になる。愚直解法とは。M 個の数について末尾の位置を求め、その最小値(もっとも前の位置)を I とする。I とはどういう数か。A 数列のうち I までの範囲からはどんな要素を選んで先頭に配置しても良い。なんなら I 番目を先頭にしても良い。そうしても1から M までの数が欠けることがないので。もちろん最小の要素を選んでこれを答えの先頭の要素とする。以降は選んだ値を取り除き A 数列の範囲を限定して繰り返し、2番目3番目……M 番目の要素を選んでいく。■提出 #43807822 (AC / 757 Byte / 1384 ms)。実装はごくごく素直で難しくない。Ruby によるすべての提出を見ると、セグメント木とヒープを併用するもの、セグメント木と二分探索を併用していて倍速いもの、Array#tally と while ループでゴルフをしているくっそ速いものなどいろいろ。自分のはセグメント木を2つ使っていて一番遅い部類。1つ使ったら2つ使うのも同じだと思ったけど、定数倍の面で最善の選択ではなかったみたい。セグメント木を使わない解法はさっぱりわかんない。セグメント木を使わないで N^2 にならない理由がわからない。■末尾の位置の最小値は最初にソートしておけば Array#shift で更新していける。で、その最前線に向かって増加列を作る。最前線を更新する。増加列を作る。繰り返し。みたいな?■提出 #43911137 (AC / 277 Byte / 278 ms)。そうみたい。Array#tally と、答えの配列(B)と、答えの配列のどこまでが確定しているか(i)と、値から答えの配列の添字を引く逆引きインデックス(I)を使っている。答えの配列の後半の未確定部分に増加列を記録している。最初は配列を2つ使って答えと増加列を保存していたけど、1つの配列でいいなと。そうすれば値の移し替えがいらないなと。最初は増加列の中にすでに同じ値が含まれているかどうかを調べるのに二分探索を使っていたけど、逆引きインデックスがあれば定数時間だなと。tally の使い道はある値が最後に出現する位置を知るためなので、ゴルフをしているのでなければ Array にくらべてオーバーヘッドの大きい Hash を使うことに合理性はない。すでに 213 ms の提出が存在している。■伝わらないことを承知で書くけども、「N^2 にならない理由がわからない」と書いていたときには、増加列を途中までコミットするという操作が欠けていた。どういうことか。各数字の末尾の位置の最前線に到達したときに、必要に迫られて増加列を答えの数列の一部としてコミットする。そのときにコミットした最後の要素の次の位置からまた増加列の作成を開始するなら、それは手戻りであり線形時間の処理では済まなくなる。増加列の前半だけを答えにコミットして後半を残しておくことで、増加列の作成を現在の位置からそのまま継続することができる。手戻りがない。最初はこの手戻りが解決できなくて解けなかった。次にセグメント木を思い出して解けた。しかし NlogN の時間がかかる。途中までコミットするという操作を発見してとうとう線形時間の処理になった。こういう工夫のしがいのある問題好き。たとえば「Simplified Reversi」(20200920p01.03) とか。


2023年07月17日 (月) 「あたま よわい」「顔 強い」 簡潔すぎる処方薬の説明にツッコミ殺到 思わずドキッとする表現に「声出して笑った」(1/2 ページ) - ねとらぼ」■これってやさしい日本語ってことなんだろうか。かえって難しいと思った。副詞を使うことも許されないの?


2023年07月16日 (日) [AtCoder] 精進。昨日あった freee プログラミングコンテスト2023(AtCoder Beginner Contest 310)-E「NAND repeatedly」(水 diff)。実は確かめるまでもなくサンプルの1に書いてあったのだけど、「⊼ は結合法則を満たさない」。だから右側からの累積和を使って左側から数え上げていくことはできない。できない? 昨日は左側からの累積和をどうにかしてうまく数えられないかと四苦八苦していたが、成果なし。一日経ってあらためて NAND について解釈してみた。右側から0を作用させるとき、左の値によらず1になる。右側から1を作用させるとき、左の値が反転する。右側から0を作用させたときに無条件に値が決まるから結合法則が成り立たなくなったりするんだろうか。ありがたくない性質。だけど f(i,j) を考えるとき、i と j のあいだに 0 があるなら i の値によらず f の値が決まるということでもある。i から j に向かって値を作用させていくとき、0の位置で f 値が1に固定されるわけなので、以降は共通。■提出 #43667957 (AC / 450 Byte / 528 ms)。コメントに書いたけど、0 の位置をまず調べて、その隣接する位置を i<j とするとき、「f(,i)=1 として f(,k) (i<=k<j) の和」「それの累積和」を考えた。できないと思ったけど結局右側からの累積和で解いている。現在の位置より右にある最初の0以降を範囲の右端とする f の値は累積和を引いてわかる。最初の0までにある1の連続を右端とする f の値は 01010... か 10101... のどちらかなのでこれもすぐにわかる。■昨日は考えずにテキトーに累積和というのか4つの値を記録する DP というのかをがちゃがちゃしていたのがまずかったな。いや違う。考えてはいた。袋小路だっただけで。Ruby によるすべての提出を見ると、自分のは長いし遅いし、かなり回りくどいことをしているみたい。えっっっ?■F 問題「Make 10 Again」は正解方針が引けなかったな。「確率とか期待値の問題って方針を決めるところに難しさがある。正解方針が引ければ答えが合うけど、答えにたどりつけない方針を引いてしまいがち。当てもんをやっている」。D 問題「Peaceful Teams」を踏まえて和が 10 になる組み合わせを全部列挙していいのかと思ったけど、包除がうまく扱えなかった。和が 10 になるまでの DP もやったけど合わない。たぶんだけど「~が存在する」確率を尋ねてはいけないのではないかと思う。解けないから。作問者は反省してほしい。■D 問題までも書いておこうね。自分のすべての提出。AC までに使った時間が A=1分半、B=7分半、C=3分、D=29分。以下ふりかえり。■A 問題「Order Something Else」。候補は2つ。定価もしくは余計なものを買って割引価格。■B 問題「Strictly Superior」。価格とスペックの比較。価格で負けていなくて機能で勝っているか、機能で負けていなくて価格で買っているか。100 ビットのビット演算でやったけど、機能の比較が間違いやすそうですごく緊張した。f1==f1&f2 とか f2==f2&f1 とか、いかにも取り違えそう。幸いペナルティは免れた。■C 問題「Reversible」。順逆を区別しない文字列の同一性判定。辞書順で先か後かどちらかを順逆の代表にすると決めて Hash に突っ込むだけ。■D 問題「Peaceful Teams」。これが時間内に解けた最後の問題。制約が 10 以下と小さいけど、さすがに T(=10) の N(=10) 乗は許されない。どうやってチームを作ろうか。最初は仕切りを使って T チームの人数配分を決めた後で N 人の順列を当てはめようとした。たとえば N=4人、T=3チームのとき、チームへの人数配分が 2-1-1 の場合を考えると、そこに配置する人の並びが 12-3-4、12-4-3、13-2-4、13-4-2 などになる。だけど重複がありますね。2人チームに人1と人2を割り当てる場合と人2と人1を割り当てる場合を区別してはいけない。まだある。2つある1人チームに人3と人4を割り当てる場合と人4と人3を割り当てる場合を区別してはいけない。同一人数のチームの並びと、同一チーム内での人の並びを区別しないようにサイズの階乗で割り算する必要がある。そうやって個々のケースに対応するなら場合の数を考える意味がない。相性問題への対処もわからない。もう手作業で個別具体的にチームを組んでいいんじゃないか。最終的に採用した方針がこう。T 個の空のチームを予め用意する。人1から順番に参加するチームを総当たりで試させる。そのときに相性を考慮する。N 人全員が行き先を決めたときに T チームすべてに人がいたならカウント1アップ。1つ1つ数えて間に合う制約なんです。相性チェックも総当たりで突き合わせて全然問題ない。処理の流れさえ決まればやることは愚直でいい。再帰関数を使った深さ優先探索で。これが T(=10) の N(=10) 乗でない理由は、部屋というのを構成メンバーで区別しているから。16 行目の「break if is.empty?」がポイント。空きチームが5個あるときに、5個ともに入ってみる必要はないよね。■■■E 問題。kotatsugame さんの動画を見ていました。「j を動かしながら今0のやつと1のやつの個数を求めておけばいいですね。」 あっはい(その通りですね)。(動画中断)。提出 #43692228 (AC / 144 Byte / 138 ms)。ちょー簡単だった! 脳みそがお留守だったと言うほかない。


2023年07月13日 (木) [WR250R] ヘルメットが新しくなった。新しいのが SHOEI VFX-WR ALLEGIANT TC-3 BLUE/WHITE L-size。古いのが SHOEI HORNET 銀色 L-size。現行の HORNET (オフロードタイプかつシールドタイプのヘルメットの名前)が HORNET-ADV で、廃盤商品を見ても HORNET-DS しかないんだけど、HORNET-DS の性能比較画像に比較対象として無印の HORNET が見える。それ。新しいのはシールドタイプではない。HORNET を使っていてわかったのだけど、晴れの日はシールドを上げて風を受けたいし、雨の日は雨粒がバチバチ当たって痛いので当然下げたいけど、シールドが汚れていたり眼鏡とシールドがどちらも雨粒で濡れていると見えにくさが2倍でとても下げていられないので、シールドは出番がない。だから最初からなしでいい。■10 年も経つとね、ウレタンフォームが痩せてついには黒い粉に分解するんですよ。全部が同じ物質か知らないけど、ウレタンとかポリウレタンとか名前につくものは空気中の湿気を吸って経年で劣化することが宿命づけられている。大事にしまい込んでいても同じ。ポリウレタンで伸縮性を確保している服とか、PUコーティングの簡易防水バッグとかは、大事に使うようなものではない。ヘルメットも一般的にはそうみたい(性能を維持するために買い換えるのがいいと言われる)。今のバイクを買った8年前にしまいこんでいたヘルメットを引っ張り出してきたらスポンジが粉になっていたので内装だけ買い換えたんだけど、すでに公式の販売は終了していてすぐには手に入らなかったんだけど、こまめに洗っていたからかその新しい内装がまた粉になったし、雑に外そうとしたチンストラップカバーが紙のように裂けたしで、もういいかなと。全然使わなかったシールドは外したままなくなっていたし、チークパッドは新しいのが手に入らなくてぺちゃんこのを付けてても意味がないので外していたし、プラスチック製のバイザースクリューも買い足したものが少し前にまた砕けていたし、もういいかなと。すでに十分だったよなと。たしか古いのが3万円くらいで、今はそれが倍くらい。セールで1万安くてグラフィックで1万高くて、やっぱり倍くらいした。たっか。10 年後も使っているつもりなら代えの内装とバイザースクリューを確保しておくべきだけど、バイザースクリューは5個入っていてそのうち2個が予備だった。たぶん同じだろうと今のメットから無事な1個も確保した。内装を手に入れるタイミングが難しい。一緒に劣化されては困るし、販売が終了しても困るので。ちなみに内装一式は1万円ちょっとします。これはそんなに変わってないのかな。知ってる最初から高い。


2023年07月11日 (火) [AtCoder][Ruby] 言語アップデートにより次の Ruby は 3.2.2 になる見込み。Ruby は定数倍の違いで AC になったり TLE になったりすることがままあるので、ぎりぎりの問題がどうなるのか興味がある。20230619 で解いた Good Vertices は書き方の違いで TLE と AC が分かれた問題なのでちょうどいいだろう。1995 ms でぎりぎり AC だった提出 #42753403 をコードテストで実行してみた。1998ms@271.png1058ms@322.png。わお、倍早い。他にも Ruby-2.7 で遅くて Ruby-3.1 では(ということは当然 3.2 でも)改善しているものに素数判定があって、ruby27 -rprime -e "p (2**61-1).prime?" は全然返ってこないが 3.1 では数瞬で true が返る。


2023年07月09日 (日) [AtCoder] 今日は ARC164 があった。くやちい。C 問題が AC だったのがくやしい。コンテスト成績表自分のすべての提出。以下 ABC のふりかえり。■A 問題「Ternary Decomposition」。とりあえず3進数を考えます。純粋な3進数と違うところは、各桁の上限が2に限られないということ。どういうことか。n=9 のとき、3^0 ×9個が k の上限で、3^2 ×1個が k の下限。もうちょっと書くと下限は3進数で表したときの各桁の和。その範囲外なら No。範囲内であれば、ある桁を崩して1つ下の桁を3個増やすことができる。つまり2個ずつなら調整が可能。奇数個の調整は不可能。■B 問題「Switching Travel」。白黒白黒……とパスを辿っていったとき、白白もしくは黒黒の辺によって閉路が生じるなら答えは Yes だと思った。それを BFS で実装したら WA だったので UnionFind で実装し直して AC になった。何が悪かったのかはわからない。嘘ですわかります。白黒の辺だけを考えるときグラフが連結だとは限らないので一応すべての頂点を始点にして BFS をしたのだけど、訪問済みフラグを流用していた。だから白白もしくは黒黒の辺で訪問済みの頂点にぶつかったとき、それが異なる連結成分を結んでいるのか閉路なのか区別できなかった。N 回 N 要素の配列を確保することが許されないので流用したのだけど、訪問済みフラグの値を工夫しないといけなかったらしい、BFS/DFS でやるならね。ほら WAAC■C 問題「Reversible Card Game」。B 問題の AC から1時間弱の時間が残っていたのだけど、ほぼほぼ途方に暮れていた。何が最適な戦略なのかさっぱりだった。終了 10 分前くらいから書き始めたのはこういうの。まずカードを裏が大きい(A≦B)と表が大きい(A>B)で分ける。裏が大きい(A≦B)カードについては何もすることがない。だってね、全部のカードが A≦B だったとしよう。Bob は Alice が引っ繰り返したそばからカードを得点に変えていくのが最善。だから他にカードがあるときに Alice はひっくり返さないし、Bob もまだ取らない。では A>B のカードについてはどうか。これは A-B が大きい順に Alice が A を隠す(裏が大きいカードに混ぜる)、Bob がカードを得点にするということを繰り返す。表が大きいカードが1枚だけ余ったらうまいこと考える。それがサンプルの1。なんで終了6分半後に AC だったんだよー。■C 問題はもっと単純だったらしい。「B の大きいペア数が奇数のときは、1 枚だけ低い値をとらされるようだ。低い値は何を取らされる? Alice がどうがんばっても、Bob は最もリスクの低いカードを選ぶことができるようだ。abs(B-A) が最も小さいカードを選べばいい。」 言われればそう。Alice が裏返して隠した表が大きいカードも、後半戦で裏返すそばから Bob に得点に変えられてしまうのだから。


2023年07月08日 (土) [AtCoder] 今日はデンソークリエイトプログラミングコンテスト2023(AtCoder Beginner Contest 309)があった。AC までの所要時間が A=3分(+1ペナ)、B=14分(+1ペナ)、C=5分、D=9分、E=8分、F=49分(+6ペナ)。CDE にくらべて AB の難しさが際立っているね(大嘘)。以下 A-F の振り返り。■A 問題「Nine」。横着をしようとすると案外間違えるグリッドの隣接判定。グリッドで BFS/DFS をするときの有効な移動先に相当する……とか考えたのがペナルティの原因か。「左右に隣接しているか判定してください」という問題だったのに「左右に」を読み落として上下に隣接している場合も Yes と答えてしまった。■B 問題「Rotate」。ABC305-C「Snuke the Cookie Picker」もそうだけど、図形的な操作を人間はひと目で認識できるけど、それをコードに落とし込むのは案外難しいという問題。がんばってやる。ペナルティの原因は1行目と最終行では移動の向きが左右逆だということがコードに反映されていなかった。自分の解答は Array#rotate とか Array#transpose を使うものになってるけど、i とか j とか i-1 とか i+1 とかが入り乱れると絶対に3か所は間違えるので、できるだけ添字を操作しない書き方をするようにしている。DP なんかでも添字を操作しないでいいように Enumerator#with_index とか Array#zip を使う。提出前に修正できたけど今日だって四隅付近で参照する要素を何度も間違えたし。■C 問題「Medicine」。答えは a+1 日目のどれかもしくは1日目。以上。■D 問題「Add One Edge」。入力されるグラフはちょうど2つの連結成分に分かれている。以上。■E 問題「Family and Insurance」。入力は木。この形式の入力を初めて見たのはこのとき>「制約の 1≤pv<v の解釈に一瞬詰まったけど、pv の上限が v であることで、逆向きにスキャンするだけで子から親へ順序よく処理できる親切設計だとわかった」。最近では ABC295-G「Minimum Reachable City」。有効な保険を親から子に伝播させていくか、子が親から引き継ぐかでちょっと迷ったけど、入力の形式から子が親を参照する方が素直に実装できる。その際に番号の昇順に処理することで親の処理が先で子の処理が後になることを保証できる。1代先まで有効な保険は2代に渡って有効な保険だという数字のずれに注意。もうひとつ。サンプルの1が親切だったのだけど、同じ人が複数の保険に加入していることがある。期間の短い保険で上書きしないように注意。■F 問題「Box in Box」。6ペナとバグり散らかした。方針はわりとすぐに決まって、まず入力される3つ組はソートする。縦横高さに3つ組以上の意味はないし、対角線を縦横高さにする問題でもない。3数を A≦B≦C として A の昇順に処理を進めるならあとは B1<B2、C1<C2 となる (B1,C1)、(B2,C2) を見つける問題になる。B を添字とするセグメント木に C の最小値を記録していけば見つけられる。複数の実装ミスを順番に潰していくことで WA×20→WA×11→WA×6→WA×5→WA×2 という経過をたどった。まずそんなにバグらせるなと、そしてバグ修正は律儀に1つずつやらなくてもいいんだと、言いたい。提出のたびに気持ちは G 問題へ移っていたのだけど、WA が出るたびに泣く泣くバグ修正のため F 問題に引き戻された。最初の提出にかけたのは 19 分でそのときに全体の形はできあがっていたのに、その後デバッグに 30 分かけている。G 問題を考えるどころではなく5完と6完の瀬戸際だった。■今日は冴えない日だったな。コンテスト成績表自分のすべての提出(※要ログイン)。