/ 最近 .rdf 追記 設定 本棚

脳log[2021-03-17~]



2021年03月17日 (水)

最終更新: 2021-03-24T16:47+0900

[AtCoder] AtCoder Beginner Contest 067D 問題 Fennec VS. Snuke

解いたあとで他の人の Ruby での解答を見たらバリエーションがいくつか見られた。

 解法1:キューを2本用意してフェネック、すぬけくん双方のスタート地点から各ノードまでの距離を幅優先探索などで確定し、それからノードの塗り分けをする。

これが一番多かったと思う。公式解説に書かれている通りの手順。

 解法2:1本のキューでフェネック、すぬけくんが交互に陣取りをしていく。

これは Ruby で最速の qib さんの提出 #20369253 (191 ms) の解法。

公式解説にはこう書かれている。

マス i と j の距離を d(i,j) として,マス i の色は d(1,i) ≦ d(N,i) ならば黒,そうでなければ白となる.結論としてマス 1 とマス N の 2 点から幅優先探索や深さ優先探索などを行うことで O(N) でこの問題を解くことが可能である.

解法1はたしかに解説通りの手順ではあるが、解答にあたり具体的な距離まで知りたいわけではなく、距離の大小関係だけ知れれば十分なのだ。

解法2の手順は(スタート地点からの距離を測定する)幅優先探索に則っているのだが、一見すると1手につき1マスしか塗れないゲームのルールに反しているように見えるのが難しい。同じことは解法1にも言えて、「マス i の色は d(1,i) ≦ d(N,i) ならば黒,そうでなければ白となる」が納得できるかどうかに尽きるのだけど、解法2の手順がなまじゲームに似ているせいで考えてしまう。

 解法3:自分の>提出 #20999230 (208 ms) やや遅く、メモリ消費も多い。

フェネックとすぬけくんの行動原理として想定したのは公式解説のものと同じ。見立てだけが異なる。どういう見立てだったか。

フェネック(すぬけくんでもいいが便宜上フェネックを選ぶ)のスタート地点を木の根と定めて、すぬけくんのスタート地点の深さを知る。すぬけくんは移動可能範囲を広げるために根に向かって移動する。フェネックはすぬけくんの移動可能範囲を狭めるためにすぬけくんに向かって移動する。出会うのは中間の深さ。すぬけくんは根に向かって移動できなくなった地点を根としてその子孫ノードだけを塗ることができる(だから一直線に根(フェネックのスタート地点)を目指していた)。

結局のところこの問題は一本の辺を見つけ出す問題だった。頂点集合をフェネック側、すぬけくん側に分ける辺がどれかを見つける問題だった。

その手順として幅優先探索(解法1)とその応用(解法2)と深さ優先探索(解法3)とダイクストラ法(未紹介)と、いろいろな方法があって、実行速度の差があった。同じ線形時間でも1回なめるだけで済ませられるのか、2回か、3回か。

 AtCoder Beginner Contest 148F 問題 Playing Tag on Tree

今日@2021-03-23 たまたま取り組んだこの問題が同じ方針で解けそうだった。

2地点から深さ優先探索で陣取りをしていって、中央付近でにらみ合って、それからどれだけ相手陣へ侵攻(自陣へ後退)できるかを数えれば答えになりそうだった。

 提出 #21207034 (WA×1 after_contest_01)

きっちりと隙を見せない after_contest に撃ち落とされましたとさ。

競技プログラミングをするフレンズ @kyopro_friends

サーバル「ABC148F『Playing tag on tree』にafter_contestを追加したよ! 不等式に等号を入れるか入れないかを間違ってるコードが落ちるようになったはずだから確認してみてね」https://t.co/jcHP4lHFhg

 提出 #21208328 (AC)

不等号などなかった。先攻後攻を入れ替えたのと、自陣へ逃げ込もうとしてうっかり中立地帯へ迷い込まないように道を塞いだ。

当初方針のまま after_contest に対応したが、どうにも不自然に頑張ったようなコードになってしまった。この問題に関しては、想定解法通りに2通りの距離表を見比べて答えを選び出すのが良かっただろう。

ところで ABC148 はオンタイムで参加していた。A-D まで灰 diff で、E 問題に至ってもギリギリ緑という低難度回。F 問題でやっと水 diff 中位だったらしい。当時1時間を残していながら解けなかったのがこの F 問題。何を考えて解けなかったか。

木の上で追いかけっこをする2人がすれ違うことができない、ということが認識できていなかった。だから偶奇が適切な部分木を選んで逃げ込むことで追跡が躱せるような気がしていた。それじゃあこの但し書きが嘘になるのにね。「なお、ゲームは必ず終了することが証明できます。」 そんなん考えたら青 diff 上位の「DFS Game」より難しくなるってのにね。


2021年03月16日 (火) [AtCoder] 昨日から AtCoder Problems が真っ白で困っていた。Array.prototype.flatMap を補ってとりあえず大丈夫。どのブラウザも OS を選り好みするせいで代替になり得ない。■なんかそうしたら以前から真っ白だった AtCoder Pie Charts タブまで正常に表示されるようになった。棚ぼた。 ABC や ARC の問題一覧が見られる Table ページで自分の状態(AC/NotAC/NoSub)も見られるようになっている。あれってユーザースクリプトじゃなかったんか……。


2021年03月13日 (土)

最終更新: 2021-03-15T22:56+0900

[AtCoder] パナソニックプログラミングコンテスト(AtCoder Beginner Contest 195)F 問題 Coprime Present

本日の ABC。1時間かけて ABCD の4完で、残り40分考えて E 問題が解けずに終わった。ゲーム問題苦手。勝ち筋とか必勝法とか、さっぱり見えない。「自分はこの、先攻後攻が決まった瞬間に勝ち負けが見えるゲームを、きっと楽しくプレイできるんだろうなあ。

本番中に E 問題が行き詰まっている最中に F 問題をタイトルだけチラ見していた。Coprime の単語が見えた瞬間にあきらめた。別の問題だけど先々月に「Coprime はまた解けなかった。」 完全に苦手意識を持っている。素数とか見たくない。

 提出 #20911347 (TLE×19 / AC ×17)

割と大きめのサンプル3が通ったのでいけると思ったが TLE だった。

考えたことを順番に。

  1. 制約「B−A≤72」があからさまな弱点。
  2. A と B 自体は 10^{18} になりうる大きな数なので、互いに素をどのように確かめるか。
  3. 72 以下の素数で割ってみればいい。
  4. [A,B] の区間から作ってはいけないペアが列挙できたが、これを 2^{B-A+1} 通りの組み合わせからどう除外するか。
  5. (迷走) ペアの左側をマージしたビット列とペアの右側をマージしたビット列を用意して、全 2^{B-A+1} 通りを振り分けよう……実行が終わらない。
  6. (迷走) ペアを併合したグループを使って解けないか……解けない。
  7. 愚直にカードを1枚ずつ引いて、使う場合と使わない場合で深さ優先探索を……これがさっきの TLE 提出。

 提出 #20911691 (AC / 357 Byte / 221 ms / 14628 KB)

このとき(緑diff精進3問)解いた問題の1つが「ABC 115 D - Christmas」なんだけど、素直に問題の通りに書いた最初の版が明らかに TLE を免れなくて、ださいけど if を使って2回の再帰呼び出しを1回に節約するパスを追加することで AC になっていた。

倍倍ゲームになりうる再帰構造には特別な警戒が必要だということと、それが反転したときに改善効果が劇的だということを学んでいた。今回も最後の lambda F に2行追加して AC。

 「(迷走) ペアを併合したグループを使って解けないか……解けない。」

たぶんグループの作り方が間違っていた。二次ペア三次ペアと芋づる式に相互グループを作るのでなく、それぞれの数ごとに一次ペアのグループを作って、そのサイズでクラス分けをすれば、計算で答えが求まったのではないか。計算の材料にする数字が誤っていたから求まらなかったのではないか。いやでもそのクラスには公倍数の情報が抜けてるのか……。

 「72 以下の素数で割ってみればいい。」

組み合わせた結果をフィルタリングするよりも、フィルタリングした結果を組み合わせるべきだったのではないか。SQL がそうでしょう? JOIN する前に WHERE で絞るべきなんだ。WHERE に似ていても HAVING では遅いんだ。

全探索がダメでもある種の探索が許されていたあたり、今日の制約には優しさが感じられるなあ。


これに関連した @kyopro_friends さんのツイートを考えていた。

競技プログラミングをするフレンズ @kyopro_friends

アライグマ「F問題は、COLOCON2018C『すぬけそだて――ごはん――』の難しい版なのだ! gcd(x,y)=gcd(x-y,y)≦|x-y|だから、72以下の素数の倍数が重複しないようにすればよくて、どの素数の倍数をもう使ったかでbitDPすればいいのだ!」

gcd(x,y)=gcd(x-y,y)≦|x-y|」ってつまり……

  • 10000 と 10010 のように近接した2数があるとき、その公約数が 10000 近辺にあることはないのだなあ。
  • 大小2つの数とその差(正の方)という3つの数があるとき、これらは GCD を共有しているのだなあ。
  • x-y を繰り返して行き着く先は x%y だけど、なんだかこれってユークリッドの互除法……

というような発見があった。ものがよく見えていないと「新発見」が多い。ユークリッドの互除法まで見つけてしまった。開拓者か研究者に向いているのではないか。


2021年03月10日 (水) [AtCoder] 自分はぼんくらなので最近まで気がつかなかったんだけど、(AtCoder Problems で確認できる) AC 数って2通りの意味があるんね。そしてランキングに名前が出るような人にとってはほぼ例外なく2番目の意味(……でもないか。1000 超えてても埋まってなかったりする)。解いた問題の数、ではない。解ける問題の数、である。どれだけ多くどれだけ難しい問題を解く能力を持ち、実際に解いたか、を表す数なのである。■数えてみたら自分の AC 数は 1000 を超えたところで頭打ちになる。そこが第2のスタート地点と言えるだろう。解ける問題を増やしていくつらい道のりの。


2021年02月24日 (水)

最終更新: 2021-03-23T20:00+0900

[AtCoder] SOMPO HD プログラミングコンテスト2021(AtCoder Beginner Contest 192)F 問題 Potion

解説を読んで ABC をコンプリートしようシリーズの1回目。ABC192 で残っているのは F 問題。いわゆるポーションって portion とはスペルが違ったのね。

2回目があるかはわからない。1回目にして解説を読んでから2日間苦しんだ。DP だったんだけど、人類が扱うには次元が高すぎるのではないかな? 自分には無理。

 提出 #20468517 (AC / 614 Byte / 1451 ms / 18204 KB)

ソースコードの冒頭にも引用したけど、解説の要諦が次の一文。

dp[i][j][k] = i 番目までから j 個選んだときの和であって、mod C で k に等しいようなものの最大値

自分は最初これを次のように解釈した。

dp[i][j][k] = i 番目までから j 個選んだときの和であって、mod j で k に等しいようなものの最大値

微妙な違いがわかりますか? mod C と mod j の違い。うっかりミスではなく、理解できる範疇を超えていたから、これってこういう意味だよね、と一段次元が低い誤った理解しか生まれなかった。

引き回すデータ配列の構成を教えてもらってさえ遷移が書けるまで一日かかったんだけど、いざ完成したらこの微妙な勘違いのせいで時々答えが合わなかった。時々。答え合わせに使ったのは次のナイーブな解答スクリプト。N が 30 を超えると実行時間が現実的でないので生成する入力の N は小さめに。テストケースはまだ利用できない。

N,X,*A = $<.read.split.map(&:to_i)

p (1..N).filter_map{|c|
	k = X%c
	m = A.combination(c).map(&:sum).sort.reverse_each.find{|m| m%c == k }
	(X-m)/c if m
}.min

要するに、これを時間制限に収まるように書き直しましょう、という問題だった。それが難しい。

結局一度完成したと思ったスクリプトを囲うようにもうひとつループを重ねた。法が変わると余りは再利用できない。最初から目的地(C)を定めて j を変化させなければいけない。dp 配列の添字 k の上限は j でなく C である。無理だよ、明日にはもう自分でこの文が理解できないよ。


DP であることでナイーブな解答より有利になる点は次の2つ?

  1. j+1 個の組み合わせを生成するのに j 個の組み合わせ結果が利用できる。

    その際にキーとなるのが添字 i (「i 番目までから j 個選んだときの和であって」)。j 個の組み合わせ結果を i (1~N-j)によって分類しておくことで、j+1 個の組み合わせを作るのに利用できる。

    たぶんこれって DP のひとつの典型なんだと思うけど、配列の型を示されてさえこの種の遷移(何を残して何を再利用するか)を見つけるのに1日かかった。

    見つけた遷移は具体的には、「j を C まで増やしながら、ある j について i 番目の要素(A[i])を i の大きい順に考える。A[i] を採用しないときに dp[j][i] に対応する C 要素の配列は dp[j][i+1] のものと同じ。A[i] を採用するときは dp[j-1][i+1] に記録された C 個の値と組み合わせる」 i と j が解説とは入れ替わってら。

    dp[j][i] の値を作るのに dp[j][i+1] (最内ループの直前の値)と dp[j-1][i+1] (中間ループの直前の値の1要素)を再利用している。

    勘違いして見えていなかったのは、j=C であり j を 1..N の範囲で変化させる過程で各 j(C) に対応した答えが見つかる……のではなく、C=1..N について j を 1..C の範囲で変化させなければいけないということ。

  2. 組み合わせた結果の和を mod C で分類して最大の値だけを採用することで、無駄な組み合わせが省ける。
  3. あとはまあ、組み合わせる数は多い方が初期値の点でも経時増加量の点でも有利なので、ループを降順にして、途中で打ち切り条件を設定したりした。だけど special_xx.txt に類するケースがスペシャルな理由は、こうした打ち切りが無効なところにありそう。

  • 提出 #20486969 (TLE×11)

    主にイテレータを使って書き直したので遅くなるのはわかる。

    Array#min の代わりに Array#[] でダイレクトに最小値を取得するようにしたので、special_xx.txt 以外のケースでは改善している。

  • 提出 #20486969 (TLE×11)

    同じように Array#min を使うのをやめたのと、イテレータを使わず全て while で書いた。special_xx.txt 以外のケースで上よりさらに少し改善しているが、TLE は TLE。

    Ruby って整数演算が足す引く剰余大小比較まで、どれも同じくらい遅い雰囲気。演算コストに差がないなら演算子の数を減らす方がいい?

    でもどこに 800 ms も遅くなる要因があった? もう予測できない。

 提出 #20581154 (AC / 615 Byte / 1437 ms / 15868 KB)

平均すると最初の提出より1割弱タイムが改善しているけど、意味のある差ではない。

ベースはイテレータメインの提出 #20486969 (TLE×11)。

AC と TLE の分かれ目は4重ループの最深部にあった。

  1. 初期値を正の無限大ではなく nil にした。

    正の無限大は正常値として扱えるので記述が統一できるのだけど、むしろ異常値として nil や -1 や無限大を設定・検知して、ループをスキップするのが良かった。

    ところで、想定上限を整数で表現しようとすると 67 か 68 ビットが必要になる気がして採用できなかった。Float::INFINITY と Bignum の、どちらがいいともいえない。打ち切り条件が ×C ではなく ÷C である理由でもある。

  2. 余り(k)の計算は、k = m%ck-=c if c<=k よりも、「実行されないコードが最速」なのだった。負の添字を使った配列参照は組み込まれた機能でありコストは支払い済みなので、使い倒さなければ損になる。

いくつかの C について最小公倍数で余りをとれば、より外側のループで DP 配列が再利用できるのではないか。数列 A の偏りと C の組み合わせを調べれば、k が取り得る値が C 種類より少なくなるのを見抜けるのではないか。結局のところ、TLE の原因はおそらく X%C と A%C(の和) がまったくマッチしないせいで4重ループを最初から最後までフル回転させられるせいだと思うから。

 提出 #20639856 (AC / 870 Byte / 1167 ms / 24016 KB)

「いくつかの C について最小公倍数で余りをとれば、より外側のループで DP 配列が再利用できるのではないか」を実装してみた。話を単純にするために C が偶数の時に j=C/2; i=0 の DP 配列を C=C/2 の DP 配列として再利用した。

たとえば N が上限の 100 のとき、51..100 は普通に DP をする。1..50 は再利用配列を使用して DP をしない。限界は次の2点。

  • C が大きいときの方が DP の処理量が多いので、全長の下半分の節約は、処理時間にすると4分の1以下の短縮効果にしかつながっていない。
  • special_xx.txt 以外のテストケースでは打ち切り条件が有効に働くので、DP の節約効果が日の目を見ないどころかただオーバーヘッドを増やしただけになる。

 @2021-03-05 テストケース

ケースXX (素因数)A に含まれる 9999999 の数答えが見つかる C
special_01.txt52142908377193267103×4703×10764231956301
special_02.txt48620189947792921131×2719×18713×729445312
special_03.txt70227681074731923770227681074731923723
special_04.txt651020109319638361162011×231599×1735054934
special_05.txt61168850281850484182936769××737535968945
special_06.txt857415171960730822×11257×32587×11686759956
special_07.txt794433313787770441101×74910361×10500118167
special_08.txt515779426304609041101×510672699311494178
special_09.txt8962979337589569513×22769×1312161174929389
special_10.txt908429522499966622×24335153×1866496427910

N はすべて 100。数列 A の要素はほとんどが 10000000 で、0から9個が 9999999 という構成。

special_xx.txt が入力する数列 A の中に値の種類は1から2個しかなかった。C 個選んだ和の余りがとる値は、限られた 9999999 がいくつ含まれるかでしか違いが出なかった。つまり1から10種類。それでも C が 1..N の範囲で変化するうちに余りの数字(k)自体は変化していくし、X%C も変化するんだけど、どうやったらぎりぎり最後までマッチングしないような X が選べるんですか?


2021年02月21日 (日) ARC の方。A だけ。B 問題は通せへんとあかんかったな(最終的に WA が2ケース)。C 問題は読んでないよ。なお、AtCoder Problems によると C 問題でもギリギリ緑色という難易度のよう。C 問題までさっくり通せるべきだったね。C完B完。これをコンテスト時間中にだね……。

最終更新: 2021-04-06T17:58+0900

[AtCoder] SOMPO HD プログラミングコンテスト2021(AtCoder Beginner Contest 192)/B,C,D,E

昨日あった ABC。今晩には ARC があるので復習が忙しい。

 B 問題 uNrEaDaBlE sTrInG

正規表現を乱用する問題だと決めつけて考えた。使える限り最善でなくてもかえって難しくなっても正規表現を使う。

 提出 #20290278 (AC / 50 Byte / 62 ms / 14440 KB)

パターンには改良の余地がある。たぶん /^([a-z][A-Z\n])+$/ で良かった。

$ は改行の前でも文字列の末尾でもマッチしたと思ったけど、フラグの影響がどう出るかが不確かだ。そして Ruby のフラグは JavaScript のフラグと比べてあべこべな雰囲気がしてわかりにくい。

入力が英大文字小文字だけだから大小の判別は1ビットを見るだけでいいんだけど、正規表現だから関係ない。

 C 問題 Kaprekar Number

C 問題にしては……と疑いをもったが、テキトーに大きい桁を与えてもいけるみたいだったので問題の通りに関数 f を定義してシミュレーションした。本当はテキトーに大きいだけだとすぐに桁数の少ない値に収束してしまいかねなくて、そうではない嫌らしい値が与えられるかもという疑いがまだあったのだけど、とりあえず投げてみるスタイル。

 提出 #20296774 (AC / 157 Byte / 560 ms / 14324 KB)

最近誰かがツイートで Integer#digits メソッドに言及していたので初めて使ってみた。適所では? そういえば D 問題でも使っていた。適時(タイムリー)だ。

 D 問題 Base n

やってきました因縁の D 問題。前回の虐殺劇が記憶に新しい>20210206p01.02。今回も E 問題が緑色なのに対して D 問題が水色だったりして、正答数に逆転があったもよう。

あれ? やるだけ? という感想はあまりに素直。たしかに優秀な人は目をつぶっていても答えにたどり着けるのかもしれないが、凡人は周到に落とし穴を探し出さなければいけない。

 落とし穴1:基数が変われば必ず得られる数が変わる?

それは1の位についてだけ当てはまらない。基数が3でも4でも5でも、数1は0より大きい1番目の数で変わらない。

そしてこれが、基数の種類を答える問題でないことの傍証になる(そういう誤読が多かったらしい)。無限大の答え方が問題文中にないからだ。

 落とし穴2:その式、d+1 と M(+1) に暗黙の大小関係を仮定していませんか?

二分探索の下限に d+1 を、上限に M+1 を設定していたのだけど、M+1 の方が d+1 より小さいことがあるから、答えを導く引き算の結果が負の数になるケースがあった。

手で計算しているときは自然と自然数の範囲でものごとを考えてしまって例外ケースを無視してしまいがち。

早期に AC を得ていた複数の人が上限を定めない二分探索を行っていたようだ。kotatsugame さんがこの奇妙な二分探索の振る舞いについてツイートしていたので存在は知っていたが、自分で使えるほどには知らないし思い出さない。

 提出 #20335649 (AC / 212 Byte / 64 ms / 14580 KB)

7 WA のあとの AC。どちらの落とし穴もテキトーな入力を与えて出力を見るデバッグで発見した。1桁のケースはタイプするのも簡単だし、それでいて境界に近くてバグが潜みがち。嗅覚を働かせよ。

えびま @evima0

(D 実はもともと「9 1」っていうサンプルがあったんですが、出題の意義が 1/3 くらい消滅する (「1 9」だと 2/3 消滅) ので消してもらいました) https://t.co/NNcbAu6GjF

このツイートはもっともで、そうでなければ D 問題としては易しすぎて出題されなかったと思う。といってもこれだけいくつも罠があって目配りが要求されるなら、AGC の A 問題といった風情もある。

自分より上位の人は仮に D 問題の罠にはまったとしても、さくっと E 問題を片付けてから帰ってきて、結局 E も D も通してしまうというムーブができてしまう。(そもそも罠にはまらないか)それができるからこそそのレイティングなのだ。自分がそれをやろうとすると虻蜂取らずになるのが目に見えているので、1時間かけて D 問題を通しました。今回の成績はABCDの4完最遅レベルでレートは横ばい。

競技プログラミングをするフレンズ @kyopro_friends

フェネック「もともとD問題でXが1文字のケースを、アライさんは2個か3個しか用意してなくて、それだとWAのケース数でコーナーケースがバレそうだからたくさん増やすようにアドバイスしてみたんだけど、どうだったかな?」https://t.co/FxcvbhUJNL

AC が出るまでは WA の数を見て方針を疑ったり挫けそうになったりしたけど、1アイディアで 7 WA が 1 WA にまで減ったりしたから、まあこういうことなんだろうと予想はしていた。まんまと手のひらころころ。

 E 問題 Train

プライオリティキューを書くだけの問題。まあその「書くだけ」ができなくて 2 WA するのが自分なのだけど。

 提出 #20371092 (AC / 998 Byte / 594 ms / 48260 KB)

……だと思ったら、Ruby で最も速い複数の提出が Hash を待ち行列に使っていた。keys.min で最小値を都度取り出す使い方で、それでいて速い。えええ?

あと、久しぶりにプライオリティキューを書いたから速度改善テクニックを1つ忘れていた。ヒープを整理するときに都度都度要素を交換しながら上昇(下降)するのでなくて、ローテーションする感じで、いくつかの要素を順番にスライドさせてできた空きに追加要素を置くのがいい。


2021年02月19日 (金) 「デルモンテ」が「ピンク色のパイナップル」を発売! | TABI LABO」 ピンク(の)パイナップル。略して……。


2021年02月16日 (火)

最終更新: 2021-05-07T14:00+0900

[AtCoder] AtCoder Regular Contest 112/A,B,C

先週末の ARC。ABの2完でレートは横ばい。ちょっと背伸びして C 問題が今やっと解けたので日記にする(べつに考え続けていたわけではなくて、オラクルが降ってくるのを待っていたのです)。

 A - B = C

 提出 #20145565 (TLE×8 / AC×4)
 提出 #20146816 (AC / 96 Byte / 93 ms / 14500 KB)

一応制約は掛け算していたんだけど、まずは素直に数えて確実に答えを……TLE。見れば一次 の k のΣなので機械的に変形して……AC。

間違った式の変形に5分以上の時間をかけてもしゃーないので、TLE は避けられない。今は(ARC の1問目に対しても)ステップを刻まなければ、答えにたどりつくことさえ覚束ない。

 B - -- - B

どれだけ1円を払っても数の種類は1しか増えないので、基本となる操作は何回2円を払って絶対値を変化させられるか。±B が境界として存在していて、|B| から 0 へ向かう変化と -|B| から負の無限大へ向かう変化が考えられる。反対側の値は1円余らせておくだけでいい。0 を挟んで -B から B の範囲を数えるのが面倒か。親切にも B=0 となるコーナーケースがサンプルのひとつになっている。もうひとつのコーナーケースが C=1

 提出 #20157041 (AC / 204 Byte / 63 ms / 14300 KB)

2 WA のあとの AC。±B と 0 と、それらで区切られた4つの区間を愚直に数えた。

 提出 #20182663 (AC / 145 Byte / 65 ms / 14268 KB)

翌日になって機械的に式を整理したもの。できれば if による分岐を消したかったのだが。

 C - DFS Game

問題の見通しは難しくない。表のスコアと裏のスコアと、手番を渡すか否かのフラグ(子孫ノード数(=スコア計)の偶奇)があって、それらを葉からボトムアップで積み上げていけば先手、後手のスコアが即座に解る。

制約の 1≤p_v<v の解釈に一瞬詰まったけど、p_v の上限が v であることで、逆向きにスキャンするだけで子から親へ順序よく処理できる親切設計だとわかった。

最後まで解らなかったのは青木君高橋君が採用する最適な行動がいかなるものであるか。二人が何を指標にしてどの子を選ぶのか、それが解らないでどうしてコーディングができる? 何をコードにする? 自分はこの、先攻後攻が決まった瞬間に勝ち負けが見えるゲームを、きっと楽しくプレイできるんだろうなあ。

 提出 #20211491 (AC / 568 Byte / 352 ms / 49992 KB)

odd.sort_by!{ _2-_1 }even.each(...) がキモ。これが二人の戦術。


最後まで見えなかった even.each についてもう少し。

even は潜って戻ってきたときに手番が入れ替わらない子ノードを集めた配列。表のスコアが裏のスコアより高いものは手番(※広辞苑にはテツガイの見出ししかない。テバンは業界用語か?)を持っている方がさっさと潜って表のスコアを得てしまえばいい(裏のスコアは相手に渡る)。では裏のスコアの方が高いものは?

裏のスコアの方が高いものは、できることなら相手の手番で相手に選ばせたい。そうすれば表のスコア(低い)が相手のものに、裏のスコア(高い)が自分のものになる。それが可能になるのは、潜って戻ってきたときに相手に手番が渡る子ノード(odd 配列)が奇数個ある場合。手番というババの押し付け合いに勝てる。

 提出 #20212523 (AC / 432 Byte / 255 ms / 54588 KB)

Ruby の他の AC 提出(今のところ2つ)と比べて遅かったので出し直し。100 ms 縮んで遜色がなくなった。省メモリを目論んだが結果的に増えている。配列の配列の配列がよくない。

ところで、再帰呼び出しを行っている解答を手元で実行してみたらいくつかのケースで stack level too deep (SystemStackError) が出て速度比較ができなかった。PC が貧弱なんだな(環境変数か? その解決法はドーピングぽい)。


2021年02月13日 (土) [COSMOS] PC で再生する音楽やアニメの音が泡だったように、微妙にずれて二重に聞こえるようになった。テンポも遅れる。読み取り不良の DVD プレイヤーで同じ症状が出たことがある。光角形ケーブルを抜き差ししてみたが改善しない。再起動したりオンボードサウンドのドライバを更新しても変わらない。TxBENCH で SSD と HDD のベンチマークをとろうとしたら、HDD(音楽ファイルが保存されている)の計測に明らかな問題がある。測定のための 500 MB のファイルを書き込む前準備の段階が、10 分たっても一向に残り 480 MB から進行しない。chkdsk を実行したら何の問題も見つからなかったが、音楽再生にとどまらない PC 全体のスローダウンが解消した。ときどきイベントビューアにディスクのコントローラエラーが記録されてるのが怖いなあと思っている。「システムドライブを SSD に移した」ことでディスク関連のエラーは消えるかと期待していたんだけど、コントローラエラーが継続している。あと最近なぜか(Vista Ultimate のオプションだが)インストールせずにおいた BitLocker のエラーメッセージが新たに記録されるようになった。BitLocker-Driver が「暗号化されたボリュームのチェック: D: 上のボリューム情報が読み取れません。」と報告する。D と E と G ドライブについて。そんなドライブを割り当てた SSD も HDD も USB メモリもメモリーカードも存在しないし繋げてもいない。Windows Update から解放された枯れたシステムで、よくわからないことが起こるものだ。全体にボロなんだな。■■■再発。今度は chkdsk が24時間経っても終わらない。やり直しても同じ進捗%で止まる。3度目で完了。問題は見つからず。


2021年02月12日 (金) Firestoreの解説通りに`doc.exists`で存在を確認してから`data = doc.data()`で取得しているので、undefinedになることはないはずだが、TypeScriptはそんなこと知らない。そこで例外を投げることにする。」■さらっと「そこで例外を投げることにする」と続いているけれど、ここは思想の違いが現れる部分だと思う。自分が同じ立場なら「TypeScript は馬鹿」って言いかねない(「実行時エラーをコンパイルエラーにしたいのだとしよう。だけど視野が狭い。こちらは gets が nil を返すかどうか、そのような入力が与えられるかどうかに関する知識を持っており、nil が返るような入力が仕様違反であることを知っている。バグがあったときに修正すべき対象は Crystal のソースコードではなく、そのような入力を与えた外部にある。事の決定権はコンパイラにはない。」)。■ところが今回は立ち位置が異なっていて、(そう書いてあったからといって)ドキュメントは undefined でないことを保証してくれないよね、doc.exists で存在確認をした瞬間と doc.data() で内容を取得した瞬間は異なる世界に属しており、doc.data() が undefined を返すかどうかは doc.data() を呼んでみるまでわからないよね、という考え方をする。他プロセスと競合するファイル処理、他スレッドと競合する並列処理ではだいたいそうだと思う。■これは一貫性のないご都合主義なのだろうか。それとも競技プログラミングというルールに支配された世界と理想から離れた現実世界の違いを反映しているだけだろうか。この件ではたまたま TypeScript がしてきた指摘に異議はなかったけど、俺は差し出がましい道具に勝手に判断を下されること、選択を縛られることが嫌いだ。さっきリンクした日記から>「人類の手には多少余るとしても、プログラマを信頼し、力を与えてくれる言語が好きだ。安全のためと称して枷をはめようとする言語は選ばない。安全な(なまくら)は退屈だ。」■git は素直でパワフルな道具>20181118p01。リンカは素直で馬鹿な道具>20181102。Nintendo Switch の UI>「「あまりに親切すぎるUIは冗長になる」という考えのもと、プレーヤーを信頼して意図をはっきりさせたかった」。ユーザーの意図を反映するかわりに押しつける道具>20150410


2021年02月11日 (木) 英語力年齢診断・改 - 豪鬼メモ」■3歳から20歳までの結果が出るらしい。14歳(1回目)15歳(2回目)。引きが良いなと感じていて +1 歳だから、16歳以上の結果になることはないだろう。ハッシュタグの検索結果を眺めていると、これは低い成績ですよ。■インターネットに東大関係者多すぎ問題と勝手に関連づけて結果を軽く受け止めたい。■■■16歳(3回目)18歳(4回目)。順調に年をとっている。勘が冴え渡っているな。あと「わからない」を選ぶのをやめた。■回答一覧の単語リストが完全に入れ替わっている。正誤の記録しか残っていない。


2021年02月10日 (水) 最近読んだマンガでベルリンについて「ドイツの首都なのに……(直通便がない)」と書かれているのを読んで、え? ボンでは? と思った。「分断時代の1949年から1990年まで西ドイツの首都であり、ドイツ再統一後も首都機能を分担する。」 もう30年になるみたい。これじゃあ日本に江戸幕府があってもしかたないね(そんな話を聞いたわけではないが)。


2021年02月08日 (月) [Ruby] while queue.empty?.! というループを見つけて、二度見では理解できなくて三度見直した。見たことのない字面だから nil許容演算子(&.)なのかと最初は思った。ドットノットが出てくるのってすごい。最初に while ! queue.empty? が浮かぶでしょう? そうしたらそこから until queue.empty? へも至るでしょう? 演算子にドットを付けて呼び出そうっていうステップが割り込む余地はないように思う。そういう書き方のパターンを考えたことがなかった。いやまあ自分は最初に until が浮かぶので(「キューが空になるまで繰り返す(until queue.empty?)」)、引き出しのひとつとして蓄えたうえでしまいっぱなしにするんだけど。