/ 最近 .rdf 追記 設定 本棚

脳log[2023-03-19~]



2023年03月19日 (日) [AtCoder] 今日は ABC294 があった。コンテスト成績証自分のすべての提出。F がほぼ既出の問題(水 diff)だったからなのかは知らないけど、6完でも渋いパフォーマンス。■D 問題「Bank」。この手の、前提知識なしで非効率的ではないデータの持ち方を考えさせる問題好き(賢くて効率的なデータ構造を知識なしで再発明することはもちろんできない)。「Notebook」とか「コンテナの移動」とかも同様。「Simplified Reversi」なんかは工夫した単純なデータの方が BIT を使った場合より log が落とせたりして最高(@chokudai「F問題とても好きな問題なんだけど、データ構造でいくらでも殴れちゃうのが残念……O(N)で解いてみてね><」)。実装しながらもずっと考えていて、検索してみても空振りだったのは、窓口に現れたということをメモする変数の名前。これが一番難しくて頭を使った。適切な名前が付けられたかどうかは知らない。他の人の提出を見て気が付いたけど、数字1つで済ませられるところで自分は配列を2本使う無駄をしてしまっている。■E 問題「2xN Grid」。数字の変わり目を2列で混合してソートして順番に見ていけば一致を数えられる。コンテスト中に不思議なことがあってね、サンプルの3だけ答えが合わないと思って、デバッグプリントを仕込んで数え違いがないかどうか順番に調べていったら手計算と完全に一致していたの。じゃあ自分の問題解釈が間違っているのかと思ってふと見たらサンプルの出力例とプログラムの出力も一致してるの。どこに間違いがあったのか。目か?■F 問題「Sugar Water 2」。「食塩水」とほぼ同じ問題。以前に解いています。「これまで何度も解こうとして解けていなかった問題。(中略) 最終的な濃度を決め打てば各溶液ごとに溶質の過不足が具体的な重さでわかるので……ということでした。」 自分では解けなかったんだね。■G 問題「Distance Queries on a Tree」。やることは難しくない。効率的にどうやるか。HLD の名前を聞いたことはありますよ。コンテスト中は「絶対に初心者でもわかるHL分解/Heavy-Light-Decomposition - Qiita」を読んでお勉強していた。見覚えがあるので以前にも読んだことがあるらしい。具体的な実装方法については知らないまま、同ページによると「かなり重い」らしいですが、雰囲気でまねごとをしてみた。ヌルポみたいなエラーを直せばサンプルが合ったので提出してみたら TLE×18/AC×55 だった。5秒で TLE なのなら頑張るけど 500 秒で TLE なら頑張ってもジャッジに負荷をかけるだけなので考えてしまう。■あ! 部分木のサイズでなく辺の重さで第一子を選んでた! ちょっと待って、修正する。提出 #39886338 (AC / 1797 Byte / 3935 ms)。制限時間ぎりっぎりではあるけど無事 AC でした。■AtCoder Problems に難易度が出たね。F が青 diff 上位なのに対して G が青 diff 下位で逆転している。パフォーマンスが渋いのはこのせいか。G 問題がしっかり崖として聳え立っていれば結果は違った。F 問題について、過去の水 diff が今日の青 diff であるなら、よくいわれる傾向とは逆の結果ということになる。■G 問題。オイラーツアーで解けると何か所かで見たけど解けるイメージがわかなかったので、もうちょっとしつこく考えてみた。オイラーツアーは部分木を一列に連続して並べる方法。辺の重さの変更を部分木にまとめて適用できると何がどうなる? 根からの距離が効率的に更新できる。根からの距離がわかると2頂点間の距離がわかるか? 「頂点 1 からのパスによって決まる値だけを考えて答えを出していい理由は、たぶん LCA までのパスが相殺されるからとか?」からの連想で、LCA を使って引き算すれば求められる気がした。提出 #39906499 (AC / 1407 Byte / 2456 ms)。短く速くなった。しかもバグが .keys 抜けが1つと L[] 抜けが3つだけと実装も素直。根からの距離を BIT で、LCA をダブリングで求めた。根からの距離をセグメント木で管理するなら LCA もセグメント木で求めたらいいと思う。BIT+セグメント木は無駄だし面倒すぎるのでナシ。だってセグメント木は BIT の上位互換でしょ?■提出 #39951562 (TLE×24)。試しにセグメント木で根からの距離の累積和と LCA の両方を管理してみたら派手に TLE だった。累積和は BIT でやる方が軽いのかな。■提出 #39954631 (AC / 3966 ms)。頑張りました。セグメント木にダミーの0番目の要素を入れて添字計算の微調整を省いたり、&:method 形式は Symbol#to_proc が呼ばれるせいなのか知らないけどやや遅いみたいなのでブロックを渡すようにしたり、せっかくセグメント木で累積和を管理しているのだから根からの距離を求めてから LCA を引き算するのでなく LCA からの距離を直接求めたり、オイラーツアーで作成する配列のサイズが 3N くらいになっていたのを節約して 2N に抑えたり。最初の AC だったなんちゃって HLD よりよっぽど厳しい。■モノイドが乗せられるようにまじめに ST クラスを改良したので、クラスを書く時のマイルールを。まずクラス利用者が知りたいであろう public インターフェイスを書いてから部外秘である private メソッドを並べる。public メソッドの中では、最初に呼ばれるであろうコンストラクタ(イニシャライザ)をまず書いて、つぎにアクセサ(読み取りメソッド)を書く。最後に、なくてもいい存在であり有害にもなり得るので書きたくないミューテータ(内部への働きかけ)を書く。クラスはオブジェクトのテンプレートであり、型通りの処理(不変部分)を書く場所だけど、ではオブジェクトのアイデンティティがどこにあるかというとコンストラクタへの引数でありインスタンス変数(メンバ変数)にふるまいを変える固有の値(可変部分)がある。すべてのメソッドがインスタンス変数に依存するべきだというのはそのため。その依存がないならメソッドの置き場所が間違っているサイン。とかとかいうことを考えながらクラスを書いている。あとはまあ名前だよね。よく言われるように名前重要。ソフトウェアという形のないものにはっきりした輪郭を与えるのは名前。適切な名前さえあればその実体をどう実装するかはどうとでもなるしどうでもいい。好きに、うまくやればいい。コーディング対象として適切な抽象に紛れのない輪郭を与える唯一無二の名前を見出すところが決定的に重要(形容詞増し増しで強調しました)。自分が常々疑問なのは、変数名に型名を使うというひとつの典型パターン(var arr = new Array; みたいな)。そりゃね、飼ってる猫が1匹だけならその呼び名はネコでも十分わかりますけどね、それは消去法や文脈でそう判断できるというだけであって、直接的な命名ではない。解釈や判断という余計なステップ、疑問や誤解の入り込む余地がある。クラスではなくインスタンスの、それそのものの本質を掴んだ命名をするのだ。■いやでも適切な命名は文脈に依存するということも言えるんだよな。識別子が機能するためには他と区別するのに十分な長さを必要に応じて使うのでいい。1文字変数を使い切ってから2文字3文字変数を使う、みたいな。ハフマン符号的な。その対極がたぶん Java の説明的で長ったらしい命名で、自分は HTML の p タグとか好きでいっぱい使いたくなる人間だからどちらかといえばアンチ Java なんだな。そのくせ、よそ者として文脈を共有しないで他人が書いたコードを眺めてるときに限って「変数名に型名を使ってる」とかを批判的に見てしまうわけだ。勝手な。


2023年03月16日 (木) Twitter で AtCoder を検索した結果をよく眺めている。このところ @zenn_news の自動ツイートがちょくちょく流れてくる。べつにそのこと自体にどうこうはないのだけど(atcoder-alert に対していうべき言葉がないのと同じように!)、現状がシグナルかノイズかというとほとんどノイズに近い。なんでか考えた。まず、新着記事を紹介するのに更新日の情報はいらない。スクリーンショットが Zenn の統一スタイルでありどのツイートでも代わり映えしない。結局、ユニークなのは著者とタイトルだけなのであり、そこと新着ですよという3点から「○○さんが□□□□という記事を投稿しました」ということだけが知りたいかなと思った。そこが目立たなくなるくらいならタグと更新日はいらない。スクリーンショットも、リンクのクリッカブル範囲を広げる役には立つのかなと思ったけどただの拡大表示だったので、場所を取るだけでどうでもいい。Zenn であることを識別する役には立つかもしれないけど、やっぱり自分にとってそのことに意味はない。■ちょっと前にデザイナーが UI をデザインをするときに何を考えているか、という記事を読んだんだけど、デザイナーは見る人間使う人間のことを徹底的に考えていた。一方で典型的な技術者は情報を取捨選択したり強弱をつけたりせずに羅列して済ませていた。わかりますよ。情報を隠していいことはないし、タグ情報が欲しい人もいるかもしれない。インターネットブラウザがドメイン名を強調したいがためにホストやパスの視認性を下げたときは即座に設定でキャンセルしたし、1時間前とだけ表示された着信履歴に対して「1時間前の特定の時刻の前か後かに意味があるんじゃボケが」と日本語ではあるけどまったく日本向けにローカライズされていない Android のプリインストールアプリに腹が立つ。話を戻すと、データベースビューアならそれで正しい。でも検索結果として他のツイートと混ぜて並べられたとき、何の引っかかりもなく流れてしまう現実がある。検索する人のためにツイートしていないかもしれない。それはそう。なのでいうべき言葉はない。有用ではないけど検索結果を決まった時間で分断してしまうほど大量というわけでもないし、それに対してもできることはない。■読んでみたい記事もあるはずなのに、目には入ってもまったく頭に入ってこないなーと逆に不思議で気になったのでした。そして最近とみに増えたプロモツイートはさすがというか当然というかよくやっている。


2023年03月15日 (水) [AtCoder] 精進。ABC041-D「徒競走」(青 diff)。先月(20230228)にトポロジカルソートがわからんから解けないと書いた問題。どこで手が止まっていたかというと、サンプル3の答えが 10461394944000 とべらぼうな大きさになるにもかかわらず、停止するときに1を返す再帰関数を書いたところ。1ずつ数えて間に合う数ではない。■提出 #39755560 (AC / 237 ms)。すでにゴールしたウサギの集合をキーにしてメモ化再帰をすると何が起こるかというと、前後関係のない無関係なウサギ A と B のどちらが先にゴールしたかという経路情報がいい感じに無視される。順列総当たりが許されない制約(N≦16)だけど、A<B、A>B を区別せずに2回目以降の数え上げがサボれるなら許される。■1つだけ桁違いに速い提出がある>18 ms (#1290069)。45 行目に割り算と掛け算がある(自分は足し算しかしていない)。再帰の停止条件で 1 ではなく順列を数えて返してる。入力の受け取り方も違う。自分があるウサギをキーにして先行ウサギを記録しているところ、逆に後続ウサギをメモしている(とはいえそのことに実質的な違いはないかな)。


2023年03月12日 (日) [AtCoder] 今日あった ARC158 のふりかえり。■A 問題「+3 +5 +7」(茶 diff)。最初にわかるのは、差を2か4単位でしか詰められないから、3数の偶奇がすべて一致していないと揃えられないということ。偶奇が一致していたら、考えやすいように3つの数をすべて2で割っておいた。そうすると1回の操作である要素に1を足し、別の要素に2を足すことになる。1操作あたり3を単位として最大の要素との差が詰まるので、へこんでいる2要素のへこみ具合の和が3の倍数でなければ揃えられない。ここから2つの場合に分かれる。へこんでいる2要素の小さい方に +2、中くらいの方に +1 をしていくのだけど、最小の要素が真ん中の要素に追いつくのが早いか、真ん中の要素が最大の要素に追いつくのが早いか。簡単なのは2要素がへこんだ状態のままお互いの差が詰まる場合。この場合は1操作あたり3の最大効率で全体を均すことができる。一筋縄でいかないのがありがたくもサンプルのいの一番に提示されている、先に2つの要素が並んでしまって1つだけがへこんだ状態になるケース。1要素に +3 することはできないので、2回の操作で3つの要素に(+2+2),(+1+0),(+0+1)することになり、結果的に2操作につき3の効率で差が詰まる。という感じにステップを刻み、泥臭く場合分けをして答えを出した。提出 #39675549 (AC / 269 Byte)。本当はステップも場合分けもまとめられるものはまとめた方が良い。式が3つあればバグは3か所に潜むし、場合分けにより実行パスがテストケースごとに分かれてしまうと、実行されないパスのデバッグ機会が限られてしまう。たとえば、ツイッターでちらりと見かけたんだけど、操作を +0,+1,+2 と見る代わりに -1,+0,+1 と見ることができるらしい。そうすれば2つの場合分けが、まず2要素が並ぶまで次に3要素が並ぶまでの2ステップに共通化できそう(場合分けがステップに変わっただけで減ってないように見えるけど、さっきの提出ではそれぞれの場合で2ステップの処理をしていたので2×2が2に減る見込み)。提出 #39706817 (AC / 194 Byte)。シンプルになった!■B 問題「Sum-Product Ratio」(水 diff)。すべての要素を1以上に限定して考えてみた。分子は3要素の和で、分母が3要素の積。和より積の方が早く大きくなるから、大きい要素を使うほど分子に対する分母の比率が大きくなり、全体として値は小さくなる。逆の場合は大きくなる。本来の問題に戻ると、ここに負の値が加わってきて、さらに正数、負数の個数が3未満だったりして正負混合の場合も考えなければいけない。具体的に考えるよりも最大値最小値を作りうる極端な値(正数の最大最小値、負数の最大最小値)を 12 個まで取り出してきてテキトーに組み合わせることにした。提出 #39678373 (AC)。正直 B 問題の方が A 問題より簡単だった。■C 問題「All Pair Digit Sums」(青 diff)。解けてないよ。繰り上がりをどう処理していいかわからなかった。具体的な組み合わせを考える N^2 が許されない制約だけど、繰り上がりの連鎖は完全に個別の組み合わせに固有なので。組み合わせを考えるにあたり、ある桁について、繰り上がってきた1がある・ないと分かっている集団の内部では、桁の数字で一括りにして計算ができる。案外1の位から順番に繰り上がりの有無で分岐と分裂を繰り返していくと、組み合わせを考えるべき個々の集団は先細りで消えていったりするのかなとムシのいいことを期待したりもして。でもうまく実装できないのでは祈って提出することもできない。足し算をビット演算に分解できないか考えてみたりもしたけど、足し算は消えない。以前に本から抜き出した文が言うように、足し算は強力なのだ。「キャリーの連鎖が単一のビットをその左側にある全ビットに影響させることができるという事実から、広く認識されていない形で、加算を特別強力なデータ操作演算にしている」■■■@2023-03-13 精進。C 問題。kotatsugame さんの動画を見ていたら k 桁目を見ているときにその桁の数字と同時に 10**(k-1) で割った余りを持てばいいと教えてもらった。たとえば A={1234,9876} だとして 10^2 の位に注目しているとき、(2,34),(8,76) を処理対象にする。余りの部分は小数点以下の数字みたいなもの。言われたら、たしかにそれでできそうではあるけど、でも、そういう発想はたぶん待っていても出てこなかっただろうな。提出 #39714388 (AC / 3055 ms)。自分で書き始めたから動画を見るの中断してしまった。3055 ms は他の人の2倍3倍遅い。まだどこか頭を使わずに指を使って数を数えているところがあるみたい。


2023年03月11日 (土) [AtCoder] 精進。今日あった ABC293-E「Geometric Progression」。■最初の提出 #39640397 (RE)。M==1 のときに Integer#pow(M-2,M) は許されない。■2番目の提出 #39640942 (WA×24)。さっきの RE が AC と WA に分かれた。この提出の問題は、M が素数とは限らないのに、だから A-1 と M が互いに素だとは限らないのに、逆元を求めようとしているところ。それと最初の分岐で p X を答えにしているが、正しくは p X%M。■終了後の提出 #39648781 (AC)。mod M の世界で A-1 で割りたいけど A-1 の逆元を掛けることができない。どうするか。以前にやってるんだよな。「L 問題だけが解けずに残っていた。余りをとる M が合成数でなければ 9 の逆元が使えて解けるんだけど」。Ruby なので (A-1)*M が 70 ビットになっても気にしないよ。D 問題で時間を使いすぎて 30 分ちょっとしか使えなかったのも悪かったな。■■■D 問題「Tying Rope」。ロープを分岐なしで繋いでいった結果、輪っかがいくつあるかと直線がいくつあるかを答える問題。それだけの問題。■最初の提出 #39629872 (TLE)。40 分かけてこねこねした謎処理が TLE になりました。■2番目の提出 #39631843 (AC)。その後7分ででっちあげた UnionFind が AC でした。どうして当たり前の問題を当たり前に解けないのか。■心当たりはある。道に迷ったとき、とりあえず引き返せばいいものを、前に進んで知っている道に出ようとする子供だった。来た道通った道を2度通るのは退屈なことだ。ABC292-D「Unicyclic Components」、ABC291-E「Find Permutation」、ABC285-D「Change Usernames」というように似たような見た目の問題が続いていたので、似たような解答を書くのには抵抗がある。それで間違えてりゃ世話がないってもんだけど。■提出 #39651186 (AC)。謎処理でも通しておきました。謎っていうか単に左右のノードをたぐるだけなんだけど、だけど、定型から外れた途端に間違えるんだなあ。■■■@2023-03-14 E 問題の問題名って等比数列の意味だったの? 英語名難しすぎる(等比数列・等比級数が幾何~と呼ばれるためには何かエピソードが求められると思う。三角数……と考えてもみたけど、それはむしろ等差数列に近い)。そうすると A-1 で割るっていうのは等比数列の和の公式の分母にある r-1 (あるいは 1-r) のことだよね、たぶん。自分は A 進数で 111...1 で表される数字を 1000...0 から求めるつもりで式を立てたけど、意外な繋がりがあったもんだ(決してぼんやりしているから意外に感じられるのではない)。■■■@2023-03-14 D 問題に関連して Dancing Links という名前を初めて見たと思ったら、全然別のところでも Knuth 先生の名前とセットで見かけたりして。どうして今日一日だけそんなに有名になったのか。


2023年03月10日 (金) honto って予約注文した本が発売日に届かないどころか発売日に発送すらされないことが常らしく、そこはアマゾンが優れているところなんだよなあ。■@翌日。毎回そうなんだけど、発送メールを確認する前に荷物が到着している。発売日当日は発送メールなし注文状況も発送待ちで、翌日9時前に発送メール。午前に到着。今回はこうだった。メールには「夜間に出荷したご注文は翌日発送扱いとなります」との定型文があるけど、これをどう考えればいいのか。■可能性1。発送メールの通りに発送されたあと2時間で届いた。■可能性2。夜間に出荷され注文ステータスの更新を見逃した。発送メール送信は翌朝に繰り延べられた。翌日発送扱いになるはずだけどヤマトがすっごく頑張って翌午前に届いた。■可能性3。発送メール前日(発売日当日)の日中には出荷・配送されていて、発送メール時点では配達拠点に届いていた。注文ステータスと発送メールが嘘。■さあどれだ。あるいは4番目があるか。■あとね、些細に見えて毎回導線の途切れにひっかかってしまうんだけど、出荷メールから注文履歴に飛べない。そこがすべての起点であるのに。1クリックでヤマトのページで荷物の追跡情報が見られないのも地味に残念。どちらに至らない点があるのかはわからないけど。


2023年03月09日 (木) 今日読み始めた本が『人はどこまで合理的か?』(スティーブン ピンカー) なんだけど、「認知反射テスト」「システム1(ファスト)とシステム2(スロー)の思考」という用語で説明される間違えやすいテストが紹介されている。こういうの。「スマートフォンとケースを1つずつ買うと110ドルになります。スマートフォンの値段はケースの値段より100ドル高いです。ケースの値段はいくらですか?」 110という数字を反射的にキリよく100と10に分けると間違える。こういう問題を 20230203 のときの簡易版職業適性テスト(Gテスト)の検査 C (数理)で見たぞ。残念ながらその手のひっかけは中1のときに散々間違えたあとだ(「100gの水に 5gの NaClを溶かしてできる食塩水の質量パーセント濃度は?」という問いに 5%と答えてしまうおバカな中学一年生でした」)。20230203 のときも実は一度ひっかかったけど逆方向に検算するのが習い性なので(答えを出す前に)訂正できた。以前に「ぶつかってそれではダメだと気がついたら定義に立ち返ってひとつひとつの手順を確かめながらたどるのが方法。ここでは注意が必要だと学習したから立ち止まって確かめることができるというだけで、失敗しないうちや失敗を回避できるようになれば、やっぱり中間を飛躍して答えに飛びつく高速のパターンマッチングの出番だと思う。それが人間の基本で、トライ&エラーで最適化を重ねた結果が思慮や洗練として見えているという気がする」と書いた。2011 年のベストセラーらしい『ファスト&スロー』(ダニエル カーネマン)がすごくおもしろそう。それでね、定型発達であることの不幸は、エラーに遭遇する機会の少なさにあると思うんだ。短絡思考を修正する機会に恵まれていない。■■■@2023-03-11 日本語の話。116 ページから「20世紀前半の哲学者たちはヒュームの論述を深刻に受け止め、道徳的言説が論理に関するものでも経験的事実に関するものではないとしたら、いったいどういう意味があるのだろうかと悩んだ」。さて、この文は道徳的言説が論理に関するものであると仮定しているだろうか、そしてまた、経験的事実に関するものだと仮定しているだろうか。■「~でも~でもない」の形であれば迷いなく論理に関わるものではないし、経験的事実に関わるものでもないと読み取れる。では実際に見られたように「~でも~ではない」の形はどうだろう。自分は意味を変えずに次のように語を補うことができると考えている。「(たとえ)~で(あって)も~ではない」。そう書いてあるのだろうか。同ページの直前の文がこう「確かに道徳的言説は、論理的言説とも経験的言説とも区別しなければならない」。論理的言説と経験的言説はそろって道徳的言説と区別されるべきものだと書いてあるのであり、道徳的言説が仮に論理的言説であっても、というのは話の流れに合わない。「~でも~ではない」をどう読み取れば良かったのだろうか。もとはの違いが大違いなのではないか。


2023年03月08日 (水) パスワード/PINコード/暗証番号 コイツラの使い分けもわからんです (#4423426)」■自分の解釈。PIN と暗証番号はカードや SIM などデバイスとともに使われるもので、文字種や文字数が数字だけ4桁などに限定されがち。セキュリティは第一にデバイスを所持・管理していることによって確保する。PIN と暗証番号はデバイスを無効化するまでの時間を稼ぐ補助錠に過ぎない。というふうに理解しているのだけど、間抜けがなし崩しに窓口をインターネットに広げカードリーダーも使わず暗証番号を唯一のセキュリティに格上げしたりする。■パスワードなどの呼び名に意味はないよ。使える文字種が推定できるだけ(全国民に理解してもらうためにこれらパスワード的なものの呼び名をすべてパスワードに統一するならそれもできなくなる)。どのような使い方をされるものかで区別する。関連「2種類の秘密の質問 (20220502)」■スラドのコメントにあるけど、マイナンバーカードにまつわる「電子証明書のパスワード(マイナンバーカードの取得時に設定したマイナンバー署名用パスワード)」「利用者証明用パスワード」「券面事項入力補助用パスワード」「マイナンバーカード用パスワード」の数々はまじでわからん。ほとんどが数字4桁のパスワードみたいだから、マイナンバーカードとカードが内蔵する証明書のための PIN なんだろうけど、とりあえず全部同じ4桁にしておけばいいんでしょ?■PIN を辞書で引くと「Personal Identification Number (クレジットカードなどの) 暗証番号」だって。識別されるものは人ではないし(口座?)、識別に用いているのもカードであって数字ではない。PIN のどこに personal で identifying な要素があるのか。Card Number もしくは Device Number が相当では?


2023年03月07日 (火) Android にプリインストールされていた時計アプリの残念な点。自分は朝のアラームを2つ用意している。タップタップと両方を有効にしようとしても1つ目しか有効にならない。トグルスイッチを連打してみればわかるけど、アニメーション中に加えられた操作は無視される。そしてそれは異なるスイッチであっても同様で、あるスイッチがアニメーションしているあいだに他のスイッチを操作することができない。かったりーな。■どうあってほしいか。アニメーション中に2打目が来たら即座に遷移を完了して2打目に反応してほしい。~ほしいとか白々しく書いたけど、本心では当然そうあるべきだと思ってる。とろくさいアニメーションが人間のテンポを決めるべきではないし、機械の都合で人間の操作を捨てるべきではない。機械に指示を出した後でいちいち聞こえましたかと確認したくはない。


2023年03月04日 (土) [AtCoder] 今日あった ABC292のふりかえり。コンテスト成績証自分のすべての提出。■A 問題「CAPS LOCK」。入力の小文字を大文字にする。Ruby のこの手のメソッド名がこれまで一度で当てられたためしがない。JavaScript なら toUpperCase()。Ruby では? capitalize, toupper, uppercase, upcase さてどれでしょう。答えに行き当たらないこともままある。■B 問題「Yellow and Red Card」。0=カードなし、1=イエロー、2=レッドで管理すれば良い。イエローの 1 とレッドの 2 が入力(c)として与えられているからと分岐をひとつサボったけど、まあどうでもよろし。■C 問題「Four Variables」。N×N の組み合わせは許されないけど掛けて N 以下になる組み合わせは列挙できる。それよりも、問題文の「AB+CD=N」が4、5回読んでも理解できなかった。サンプルにヒントを求めても解答例が理解できなかった。つまり、A=12、B=34 だとして、AB=1234 だと読んでしまってそこから抜け出せなかった。A×B+C×D=N の意味ではないかとようやく推測できて、その解釈でサンプルが理解できることを確かめても、まだ半信半疑で問題に本腰を入れられなかった。問題文が難解。■D 問題「Unicyclic Components」。UnionFind をするついでに辺の本数を数えた。グラフの性質を踏まえたかっこいい解法で解きたいなと思ったけど、最近あほなので愚直にやった。■E 問題「Transitivity」。最初はダメ解法に捕まってしまった。こういうの。ある頂点を見て、入ってくる頂点と出て行く頂点の組み合わせを考える。すでに両者に辺が通っているなら操作はいらない。そうでなければ直通辺を足す。このやり方でやるとサンプルの3の答えが 17 と過大になってしまった。自分としては珍しいことだけど、そこで一旦リセットしてイチから別の解法を考えてみた(出口のない泥沼の数合わせに終始するのが見えてしまって瞬時にうんざりしてしまったのだ)。ある頂点から到達できる頂点というのは、必ず直通辺が通っていなければいけない頂点なのであって、距離2以上の頂点が操作の対象。有向辺なので問題は始点に選んだ頂点ごとに独立。足がかりに使う距離1の隣接頂点も無関係。N の2乗が通る制約なので全頂点を始点にして BFS をした。■F 問題「Regular Triangle Inside a Rectangle」。辺の長さが1の正三角形をちょっとずつ回転させて外接する矩形の大きさを調べた。そこから矩形の拡大倍率を求めたんだけど WA×10 が解消できなかった。二分探索をするのだと見かけてテキトーにでっちあげてみれば WA×43 と悪化していて AC が遠い。水 diff ですってよ。


2023年03月03日 (金) [AtCoder] 精進。ふか杯 5th Contest-D「Bintree」。制限時間が5秒だけど、ちょっと油断していたよね。■最初の提出 #39379910 (TLE×4)。ビット列で集合を管理する。重さもビット列ごとにメモしておく。ビット集合から根にする1ビットを選ぶのは BIT を参考に i&-i で。部分集合の列挙は b = bits と初期化してから b = bits&b-1 で。メインはメモ化再帰。やりやすいからこういう方法でやったけど、必要以上にテクニカルなことをしているつもりだった。しかし TLE。■2番目の提出 #39380194 (TLE×1)。これのアイディアは1つで、左の子集合(L)と右の子集合(R)に L<R という大小関係を仮定して L>R の場合の再帰呼び出しを省いた。省いた分は ×2 で辻褄が合う。■提出 #39381113 (AC / 4266 ms)。これのアイディアは2つ。1つ目は前の提出のアイディアの改善。L<R を仮定しているのだから最初から列挙回数を半分にすればいい。2つ目はコードテストで数百 ms の効果があった(けれど単体では TLE 解消に少し足りなかった)チューニング。ビット列に対応した重さを予めメモする部分のコードで、いちいち各ビットが立っているかどうか調べるのをやめた。■(オーバーヘッドが大きい) Hash を使うメモ化再帰をやめて Array を使うボトムアップの DP に書き換えるとさらに改善するだろうけど、それは遷移がわからなくて書けない。


2023年03月01日 (水)

最終更新: 2023-03-03T18:23+0900

[AtCoder] 第12回 アルゴリズム実技検定 過去問

自分のすべての提出

 A - 信号機

赤になってから Z 秒時点でボタンを押したら X 秒後に青に変わるけど、最低 Y 秒間は赤の時間が確保されているように。[Z+X,Y].max

 B - クレジット

正整数 を 100 で切り捨て除算する。桁数が 50 万と 1 になることがあるのでうっかり gets.to_i してはいけない。いや、案外平気かも。文字列として2桁削るのが無難だけど N が2桁以下のときに 0 を出力するのを忘れない。

 C - 偏ったサイコロ

出目の和ごとに場合の数を記録する DP。6×6×6×18 程度の計算量。

 D - 採点

辺の集合が与えられたときに多重辺と自己ループの有無を調べる。

強いて注意点を挙げるなら、多重辺を調べるときに文字列のまま比較すると 1 22 1 の同一性を見逃してしまうことと、隣接頂点リストを配列として持つと星型のグラフで多重辺のチェックが O(N) になってしまって全体が O(N^2) で TLE になってしまうこと。Ruby なら Hash で隣接頂点を管理する。

 E - 棒倒しゲーム

問題文に書かれている通りにスコアを消費していって、スコアに過不足がないかを調べる。

 F - 薬剤師

限られた数の薬と数限りないアレルゲンがある。薬が含むアレルゲンと人が持つアレルギーが交わらないようにするとき最も効果の高い薬の番号を答える。

制約を見ないと方針が決められない。薬は最大 100 種類。アレルゲン/アレルギーは最大 20 万種類。人は 10 万人。ただし人が持つアレルギーの総数が 10 万までに抑えられている。

人が持つアレルギーごとに使える薬を定数時間で調べて 1000 万の処理量。アレルゲンをキーにして 100 ビットのビットフラグで使えない薬を管理した。

 G - Wildcards

N が 1000 以下、L と K が 10 以下に抑えられているので、一致しているべき文字のインデックス(L-K 個)を決め打ってから文字列の集合を分類して絞り込んでいった。考えるのではなくうまく実装する。

 H - 3種の硬貨

問題文から読み取るべきこと。銀貨が有限だが無数にあると考えていい枚数ある一方、銅貨は X 枚に限られている。両替はできない。金銀銅の価値は差が非常に大きく、価値の大きい貨幣の多寡を価値の小さい貨幣でひっくり返すことができない。

なので、X 枚の銅貨をできるだけ多くの金貨に変えることをまず考え、金貨の枚数が同じ場合に使用した銀貨の枚数を少なくすることを考える。

そこまで分かれば銅貨の枚数ごとに金貨と銀貨の枚数を記録する DP をやるだけ。

 I - 毎日のリンゴ

悲しさを考える前にまず m で割った n 個の余り a%m,2*a%m,3*a%m,...,n*a%m について考える。d = gcd(a,m) とおく。m 種類の余りは周期 c = m/d のサイクル(d 個)に縮約される。それは次のようなスクリプトで可視化すればわかる。問題を解くだけなら証明はできなくてもいいでしょう。

n,a,m = 10,6,10 # d=2
p (1..n).map{|i| a*i%m } #=> [6, 2, 8, 4, 0, 6, 2, 8, 4, 0] 周期 c = 5
n,a,m = 10,10,6 # d=2
p (1..n).map{|i| a*i%m } #=> [4, 2, 0, 4, 2, 0, 4, 2, 0, 4] 周期 c = 3

サイクルの和は初項 0、公差 d、項数 c の等差数列の和なので c*(c-1)/2*d。サイクル当たりの悲しさは、余りが 0 の項の悲しさが 0 であることに注意して m*(c-1)-(サイクルの和)

サイクルから外れた n%c 個の悲しさをどう求めるか。一発で求まる式があるとは知らない。m が 300 以下の制約だから 10 万件のテストケースごとに最大 299 項の和を求めるとなると最悪 3000 万の処理量。Ruby ではちょっと厳しいかな。

n%c と c-(n%c) を比較して、n%c の方が小さいなら悲しさを足し上げる、c-(n%c) の方が小さいならサイクル当たりの悲しさから引き算で逆算することにして、最悪 1500 万の処理量ならまあまあありだと思う。同数ならどっちでもいいよ。

 「サイクルから外れた n%c 個の悲しさをどう求めるか。一発で求まる式があるとは知らない」

n%c の区間をどんどん割って余りを取って効率的に数えられるような気はする。ユークリッドの互除法くらいの効率で。でも数字が合わない。

 J - スプリンクラー

長方形と円のどちらかがどちらかを含む場合を除けば、扇型の面積から直角三角形の面積を引いたり引かなかったりすることで水を撒く面積が求まる。

扇型の面積(s)の求め方。半径を r、弧を l とすると s = r*l/2。弧の長さ(l)の求め方。中心角(ラジアン)を θ として l = r*θ。中心角(θ)の求め方。三角形の3辺の長さがすべて分かっているので、余弦定理から中心角の cos が分かり、cos が分かれば acos 関数で角度が分かる。

ここまでわかればあとは場合分けを間違えないようにやる。

 K - 連結チェック

辺を繋いで連結判定をするのはおなじみ UnionFind で。辺を切断する方法は知らない。辺を繋ぐのが 10 回以下に制限されている一方、切断する回数はいっぱい。クエリを逆向きに処理すれば切断は接続に、接続は切断に変わる。10 回の切断をどうするか。UnionFind のデータ構造を丸々コピーしても 10 万×10 = 100 万だから許される。落ち着いて頭の中を整理して逆向きに考えられたら実装するだけ。

 L - 展覧会

ヒントを見たよ。https://twitter.com/kyopro_friends/status/1630510505323540481

ポイントを抜き出せば「「最終的にmod3で何枚選ぶか」をkと先に決め打っておけば」というだけのことが独力で解決できないのだな。

基本は絵画を順番に、選んだ個数を3で割った余りが 0,1,2 のときのおすすめ度の最大がいくつかを記録する DP をやる。ここに「最終的にmod3で何枚選ぶか」が関わってくるので、(最終的な余り 0,1,2)×(現在までに選んだ個数の余り 0,1,2) = 9 通りを記録する DP をやる。答えを表示するときは (最終的な余り,現在までに選んだ個数の余り) = (0,0),(1,1),(2,2) の3通りから最大値を選ぶ。

 M - シリーズ

ある範囲のセット買いと単品買いを組み合わせて全 N 巻を揃えるのにかかる費用の最小値を求める問題。

i を増やしながら 1 から i 巻目までを揃えるのにかかる費用の最小値を記録していく DP をする。セット買いについては範囲の右端に注目する。

単巻買いの場合、1 から i 巻目までを揃えるのにかかる費用の最小値(C[i])は C[i-1]+A[i]。(A[i] は i 巻目単体の価格)

範囲の右端が i であるセット買いの場合、範囲の左端を l、セット価格を b とすると、i 巻目までを揃える費用の最小値(C[i])は min(C[l-1],C[l],C[l+1],...,C[i-1])+b。区間最小値はセグメント木にお尋ねします。

 N - 上からと横から

まだだよ。

 O - 2個のボール

まだだよ。


2023年02月28日 (火) [AtCoder] 日曜にあった AtCoder Beginner Contest 291(Sponsored by TOYOTA SYSTEMS)のふりかえり。■A 問題「camel Case」。ASCII コードで大文字小文字は特定の1ビットを見ればわかる(それ以外のビットは共通)。■B 問題「Trimmed Mean」。フィギュアスケートとか芸術競技の採点方法っぽい? ソートして真ん中を取り出す。■C 問題「LRUD Instructions 2」。賢い判定方法があるかなとちょっと気にしてみたけど、普通にメモを取ってシミュレートした。■D 問題「Flip Cards」。D は DP の D! 直前のカードが A のときの場合の数と B のときの場合の数から今回のカードが A のときの場合の数と B のときの場合の数を出す。初期値をどうするか迷った。A のときの場合の数を 1、B のときを 0 にして、答えは (A のとき+B のとき)%998244353 にしたら合っていた。■E 問題「Find Permutation」。ABC285-D「Change Usernames」を思い出す問題。自分より小さい要素がない要素を順位確定要素としてキューに追加して他の要素の前から取り除いてはキューを伸ばしていった。ただし、どの時点でもキューの長さは1でなければいけない。2つ以上の要素がキューにあるとどちらが小さいかわからない。キューの長さが1ずつしか増えないとわかったときにちょっとせこいことを考えて、キューをそのまま答えの配列にしようとした。残念ながらそのまま答えにはならなくて、順番と添字を入れ替える処理が必要だった。自分の提出にはバグがあるような気がしていて、キューが途中で途切れたときに対応できていないと思う。運良くそういうケースがなかったのかな。E 問題に関連してトポロジカルソートの語が頻出している。トポロジカルソートがわかるなら ABC041-D「徒競走」(青 diff) が埋められないなんてことはないはずなんだよなあ(まだ埋められていない⇒わかりません)。■F 問題「Teleporter and Closed off」。都市 k を通らないケースというのは、都市 k-M+1,k-M+1,...,k-1 から都市 k+1,k+2,...,k+M-1 へ飛ぶ 1+2+3+...+(M-1) 通りが上限であり、M は 10 以下なので、各 k について1つ1つ調べて良い。あとは k の手前の都市へ移動する最小回数と k の奧から移動する最小回数が1ステップでわかればいいので、前からと後ろからの2回 DP をやっておく。考察にはそれほど悩まなかったけどバグ取りをしたりしてるうちに気が付いたら 45 分経っていて驚いたよね。しかも TLE だった。2115 ms であり 22xx ms ではないから 115 ms を削る小手先の変更を2つ入れて AC。ペナルティと合わせて 9 分のロスだった。それ以前に時間をかけ過ぎていて E までを 30 分で片付けた貯金がパーなんですよ。■G 問題「OR Sum」は制限時間8秒がやばいよね。考察であっさりスマートに答えを出す系の問題ではない。あきらめちゃうよ。■自分のすべての提出コンテスト成績証。■E 問題への提出の潜在的バグについてお風呂で考えてきた。キューが途中で途切れるのはどういう場合だろうか。たとえばグラフが複数の連結成分に分かれているとき。これは始点が複数あると検出されるならバグには当たらない。しかし始点がなかったら。1つまたはそれ以上の連結成分が環状部分を持っているなら、キューは途切れる。バグか? おそらくそういうケースは「入力に矛盾しない A が存在する」という制約により除外されている。潜在的バグはバグではなかったしテストケースにも不備はなかった。そこまで見切った上での割り切った実装(9行目の if (1..N).all?{|n|)だったらかっこよかったんだけどな。ABC285-D「Change Usernames」のときも「入力制約のきれいさに助けられた」って書いてるんだよなあ。