/ 最近 .rdf 追記 設定 本棚

脳log[2021-10-27~]



2021年10月27日 (水) [AtCoder] 精進。ABC169-F「Knapsack for All Subsets」(青 diff)。よくある DP をすると TLE が必至なので、A 数列の同じ値を和と場合の数にまとめてから頑張って掛け合わせた。提出 #26852407 (AC / 798 Byte / 841 ms)。そしたらね、Ruby で断トツ一番速い提出がこれ>#13965887 (195 ms)。numo/narray ですって。それ以外の提出は 600 ms 台に先頭集団がある他は1秒オーバーがほとんどなんだけど(「Ruby によるすべての提出」)、長さに関してはどの提出も自分の 798 Byte より短い。何か見落としがある! これが短くて速い代表的な提出だけどほとんどワンライナー>#18041479。どういうことなの? ×2するところを÷2にすることで要素の更新が省略できて速いらしいんだけど、そもそも持っているデータが違うから×2も÷2もないんだよね。■繰り返される配列の確保と初期化が重かったのでできるだけ短い配列で済ませるようにした>提出 #26862342 (AC / 717 Byte / 679 ms)。考察がへぼでも 600 ms 台に入った。添字 1 から A の値未満のところは初期値から更新されないので不要なのだけど、添字 0 のところに意味のある値が入っているのが邪魔。配列をローテーションしてから長さを切り詰めている。難解。へぼな考察を実装でごり押ししているのが悪い。


2021年10月26日 (火) [AtCoder] 精進。ARC063-E「木と整数」(黄 diff)。提出 #26833318 (AC / 1419 Byte / 463 ms)。すごく、難しかったです。ノードに持たせた情報は、入力で与えられた確定数字と、数字までの距離。たとえば数字が P で距離が D なら、そのノードが取り得る値は P-D から P+D の間で1つおきの数字のどれか。これを頑張って子ノード間でマージしながら矛盾なく根っこまでたどり着けたら答えは Yes。根っこの値を1つ選んで今度は下りながら子の数を確定していく。今のところ Ruby での AC は3つなんだけど(「Ruby によるすべての提出」)、どれも異なる考え方で書かれている気がする。どういうことなんだ? さておきこれで黄 diff の AC は 10 個目。着々と増えている。■解説を読むとノードに持たせるのは数字と距離ではなく取り得る数字の範囲にすると楽そうだった。それでやると Corvvs さんのこちらの提出になるようだ>#4817029。自分でもやってみた>提出 #26837318 (AC / 722 Byte / 434 ms)。枠組みはさっきのとほぼ共通なんだけど、子ノードのマージ部分であれこれ考えずに min/max で済ませられるのがすごく楽。他にはプライオリティキューで解いている人もいますが……(根本の方針がさっぱり想像できない)。小さい数字から順番に埋めていってるっぽい。それで自然と均衡点が定まってくるというのはなんとなく想像できるけど、なんとなくだよ。そのやり方でうまくいくときは必ずうまくいくということに確信が持てない(ので自分では書けない)。こちらが参考になる>「ARC063E 木と整数(800) - tekiheiの日記」。ひっくりかえっても自分の頭からは出てこない発想。


2021年10月23日 (土)

最終更新: 2021-10-26T00:58+0900

[AtCoder] AtCoder Beginner Contest 224/D,E

D 問題で TLE に苦しんだ。E 問題も TLE に苦しんだ。そのまま E 問題が解けなかったので F 問題は読めなかった。

 D 問題 8 Puzzle on Graph

全探索が許されそうな制約。重複チェックのためのハッシュ表のキーを文字列にするか配列にするかで悩んだ。結果的に文字列ベースの探索で AC になった。

 提出 #26768646 (TLE×1 / over 4 秒)
 提出 #26770107 (AC / 2275 ms)

欠けてる数字を9番目の要素にするのがちょっとした Tips. TLE 解消の決め手にはならなかったんだけど。

現在の状態からありうる次の状態(のうち初出のもの)をすべて候補にするという意味で感覚的に全探索と書いたけど、そういうのは幅優先探索と呼ぶらしかった。

 E 問題 Integers on Grid

時間は1時間ほど残っていたのに、結果的に1時間と 10 分が AC までに必要だった。

 提出 #26776942 (TLE×18)

D 問題の TLE×1 と違って全然惜しくない。どこの処理量が膨れ上がるかとソースを眺めてみると、遷移のための辺を逐一処理しているところがダメ。

遷移の方向は A の小さい方から大きい方に決まっているので、A の降順に遷移可能回数を数え、行と列にその回数をメモしておけばいい。今後この行(この列)から遷移先を探すものがあるならメモした回数だけ遷移できますよ、というメモ。A の降順に処理しているから参照したメモはいつでも有効。

ああいや、A の値が等しい別の点が書き込んだメモを参照しないようにだけ注意が必要。この辺の呼吸は珍しくないので心得たものである。

 提出 #26781610 (AC / 1230 ms)

あと 10 分あればなあ。

 提出 #26786787 (AC / 708 ms)

不要になった処理を消し忘れてた。

レートはちょっとだけ上がってる。しかしもっと上がりたかった。


2021年10月22日 (金)

最終更新: 2021-11-23T22:07+0900

[AtCoder] AtCoder Regular Contest 120C 問題 Swaps 2

精進ですよ。水 diff だけど難しい(まるで水 diff が簡単かのような……)。考えがまとまるまで1日かかった。

 問題の操作

隣接2要素をスワップして +1/-1 するというけど、考えやすいように複数の操作をまとめるとこう。

  1. ある要素 Ai を右に(左に)いくつか移動する。
  2. たとえば右に5移動するなら、移動先の値は Ai-5 になる。
  3. たとえば右に5移動するなら、飛び越された5つの要素が、移動先に空きを作るためと移動元の空きを埋めるために、1つずつ左に移動している。
  4. 左に1移動した5つの要素は値を +1 する。

 ポテンシャル

Ai の値は移動に伴って変化しているように見えるけど、実のところ位置に応じて決まった値をとっているに過ぎない。どういう値をとるかは、最初に位置 i で値 Ai をとっていた、ということで決まる。

A 数列の各要素が位置1でとる値をその要素のポテンシャルと呼ぶことにする。ポテンシャルから要素の位置を逆引きできるようにインデックスを作成しておく。

 B 数列

B 数列をスキャンしながら、要求するポテンシャルを計算し、該当する要素を A 数列の先頭に近いところから貪欲に引っぱってくる。

引っぱってくるに際していくつの要素と位置を交換することになるかは BIT で転倒数を管理することで数える。というか、知る必要があって管理している数字が転倒数と呼ばれている、が順序として正しい。

 提出 #26732769 (AC / 561 Byte / 491 ms)

難しい。これが水 diff ってどうかしてる。ちなみに Swaps の1はこれ>「Swaps」。黄 diff です。解けるまで9か月寝かせました(20191111p0120200826p01)。2は1日寝かせただけで解けたんだから、妥当なのか?


2021年10月14日 (木) [AtCoder] 精進。ABC160-F「Distributing Integers」(ギリギリ黄 diff)。最近よく見る全方位木 DP。20210928p01.0120211010p01.06。ノード間の式は ABC217-F「Make Pair」に提出したものと同じ(#25683515)。各ノードが自身を含めて A 個の子孫ノードを塗る方法(順番列)を B 通り持っているとき、2つの子ノード以下を塗る方法は B1×B2×(A1+A2 個から A1 個を選ぶ組み合わせ)で求まる。提出 #26559753 (AC / 1028 Byte / 1548 ms)。これで黄 diff の AC は9個目。かなり貴重。■■■全方位木 DP と言えば第八回 PAST の H 問題「最短経路」。制約が N≦3000 だから全ペア 900 万通りの全探索が想定解法かなーと想像したんだけど、Ruby にはやや厳しく見える数字。最近覚えた全方位木 DP で>提出 #26574539 (AC / 72 ms)。子ノードのマージ部分が全方位木 DP 問題唯一のアイデンティティ。各ノードに子孫ノードへの距離一覧をハッシュ表で持たせた。あとは全要素更新を避けるためのオフセット値を表とは別に持たせたりマージテクなどの時間節約術。ところで、提出した解法を全方位~と呼んでいいのかはよくわからない。全ノードを起点にした距離を調べてはいるんだけど、距離って指向性がないので頂点ペアを片方向しか調べなくていいわけなので、ただの木 DP と同じように根への1パスで処理が終わっている。■BFS で全点間距離を調べる方法はやっぱり厳しかった。ゴルフしながら1秒前後で通してる提出が多数あるからそうでもないのかなって思ってたけど、実際厳しかった。あの短いスクリプトのどこにどんなアルゴリズムがあるというのか。ともあれ最終的には BFS でも通った>提出 #26823765 (AC / 814 ms)。どういう時短術を使ったか。X を超える距離はすべて X 以上という扱いにしてそれより遠くの頂点は調べなかった。その場合でも最内ループでチェックをすると TLE がひどいので(#26823520 の 13 行目)、その1つ外でチェックをした(AC コードの 11 行目) (よく考えると TLE がひどいのは距離表の更新がスキップされてるからで、X を超えていてスキップするのはキューへの追加だけにすべきだった)。最も効いたのは、すでに距離一覧を調べてある隣接ノードの結果を流用したこと。そうすると、開始点から出る辺のうちの1本から先にあるノードは距離を調べずに済ませられる。そのために距離表はオフセット値とセットにして初めて実際の距離を表す。この点は全方位木 DP でのやり方と一緒。くらべて不利な点は、距離一覧ではなく具体的な頂点とのあいだの距離を一覧して持っていて等距離にある2点を同一視できていないので扱う数字が多いことと、結果の流用が辺の1本に対してしかできていないこと。これらの差で全方位木 DP に負ける。辺に重みがあるんだから BFS ではなくダイクストラ法を使うべきだというのはあるかも。実装も定数倍も重くて気が進まないわけなんだけど。■同じアイディアでちょっと前の ABC で制約が厳しくて想定解法のワーシャルフロイド法が通らなかった問題が通せないかな。


2021年10月13日 (水) [AtCoder] 精進。ARC089-D「Checker」(青 diff)。大きさが 2K×2K で黒白2マスずつの最小市松模様に 2N 個の点をプロットしておいて、K×K の枠内に最大でいくつの点が含まれるかを二次元累積和で求めるのだと思った。概ね正しかったのだけど、二次元累積和のサイズは 2K×2K ではなくて 4K×4K が必要らしかった。わからないが言われたように修正してみる。提出 #26544471 (TLE×8)。定数倍がたいへん厳しい。提出 #26544665 (AC / 956 ms)。元データが 2K×2K なのだから愚直にデータを展開せずとも計算で仮想的に 4K×4K を存在させられる。ところでこの人の提出>#2789430。すごく短いのと、累積和のサイズが 2K×2K だ。c と N-c を同時に答えの候補にしている部分がよくわからない。わからなくはないか。黒く塗るか白く塗るかだ。そうすると 4K×4K が必要というのも、K×K の枠内が■□■□になる場合と□■□■になる場合を網羅するためだったか。じゃあ 3K×3K でも? そもそもさっきの仮想累積和の提出(#26544665)が、3K×3K の累積和上を K×K の枠が移動する 2K×2K 通りの探索になってたっぽい。わかってやってるんじゃあないんだよなあ。


2021年10月12日 (火) [AtCoder] 精進。ARC088-D「Wide Flip」(ギリギリ青 diff)。まずは問題を理解する。「操作には2つの端点がある」「端点は 0 と 1 の境界に揃えたい(←この表現)。そうすれば操作のたびに端点にあった境界が消える。さもなければ新たな境界が生まれるが、それは嬉しくない(←この表現)」「一方の端点を文字列の先頭または末尾に固定して手近な境界から貪欲に消していけばゴールに至る」「文字列の真ん中より手前側にある境界は相方に任せて良い」。提出 #26527889。■一番最初に思いついたゴールに至る確実な答えは、最も短い 111...列(000...列)の長さを操作の最小幅にすることだった。これをどこまで伸ばすことができるか。文字列の端っこまで伸ばしたとき答えが見える。


2021年10月11日 (月) ベテランの「適当でいいよ」は真に受けちゃいけない→ベテランと新人の『適当』にはこれくらいの違いがある - Togetter」■適当に正反対の意味があるのって、判断の基準が主観にあることで結果がピンキリになり、ピンの結果とキリの結果がどちらも適当にやった結果扱いされるせいで起こる現象だと思ってる。なかなかに味わい深い言葉。適当でいいという指示こそがテキトーであり適当ではなかったのであり、言葉からしっぺ返しを食らっている、的な。本当にテキトーでいいなら指示が必要な場面ではなかったはずだし、期待する結果も期待外れの結果も存在し得ないのが道理。「適当でいいよ」ではなくて「もうそれくらいでいいよ」なら許されたのかな。


2021年10月10日 (日)

最終更新: 2021-11-15T21:43+0900

[AtCoder] エクサウィザーズプログラミングコンテスト2021(AtCoder Beginner Contest 222)

大反省回。未だに ABC で ABC の3完敗退を繰り返していることに驚きを隠せない。今回がそうだしついこの前の ABC219 もそうだった>20210918。ついでに言うと3年以上前の第2回参加回もそうだった。まるで成長していない……。戒めとして普段はとばす A 問題から振り返る。

 A - Four Digits

20 年前の自分だったら N の常用対数から N の桁数を求めてくっつける 0 の数を決定していた。

だけどとある WSH 関連の掲示板で、十分な数の 0 をくっつけてから必要な文字数だけ切り出す方法を知った。

 提出 #26434564 (AC / 25 Byte)

他の人の提出を見ると printf かそれに類するメソッドを使うものが多かった。Ruby で最初に提出した人は rjust を使っていた。目的にぴったりのメソッド(rjust)がある以上、それを使うのが最善だった。

 常用対数から桁数

こういうこと。

L = lambda{|n| Math.log10 n } # 正整数 n の常用対数
D = lambda{|n| L[n].floor+1 } # 正整数 n の桁数(10進表記)

p [D[99],L[99]]   #=> [2, 1.99563519459755]
p [D[100],L[100]] #=> [3, 2.0]
p [D[101],L[101]] #=> [3, 2.0043213737826426]

学校で対数を習っても理解が伴わないと計算問題はできてもこういう風に実用できなかったりする。Project Euler の 62 問目を二重ループで解いたりもする>20110308p01.02

実はまだよく分かっていないのは自然対数の底 e。なんだかこれを底にすることで累乗が掛け算になったり曲線が直線になったりして性質を変えずに扱いやすくなったりするらしいんだけど、そういうのは(文系学部に分類されるらしいことを少し前に知って驚愕した)経済学部の人に任せておきたい。

 B - Failing Grade

コメントのしようがない。スクリプトを読んで。

 提出 #26437560 (AC / 63 Byte)

 C - Swiss-System Tournament

制約が小さいので愚直にシミュレーションをすれば良い。

解答を作成するのに 20 分かけたのが良くなかった。出力フォーマットを勘違いしていて、求められているのが順位で並べた人番号だったのに、人番号順に順位を表示しようとして余計な手間をかけ、余計な手間を実装するのにもやけに手間取ってしまった。

 提出 #26449288 (AC)

 D - Between Two Arrays

解けなかった。シンプルな DP。直前の項がとった値ごとに場合の数が記録してあれば、現在の項において取り得る値と場合の数が数えられる。それがわかっていて解けなかった。

  1. 提出 #26458829 (WA×15)
  2. 提出 #26459583 (WA×15)
  3. 提出 #26461168 (WA×15)
  4. 提出 #26493159 (WA×15) 一夜明けて今日も WA

もうね、「なんでなん?」という感想しかない。これが違うなら正解が正解ではないと言い張ることしかできない。

 提出 #26493304 (AC) Range を等価な Range で置き換えました。

制約の下限が 0 なのは知っていた。知っていたしそのせいで Range の終端が -1 になることがあるのもわかっていたが、終端が始端よりも前にある「空の Range」で切り出した部分配列が、「空の配列」ではないことがあるだなんて想像だにしなかった。これは Ruby の罠である。こういうことだ。

range1 = 0..-1 # これで WA
range2 = 0...0 # これで AC
p range1.size #=> 0
p range2.size #=> 0

array = *0..9
p array[range1].size #=> 10
p array[range2].size #=> 0

配列の切り出しにおいて Range は単なる添字のペア [0,-1] として扱われている。Range としてのアイデンティティを奪われている。この仕様を知らなかったわけではない。ただ、認めがたい仕様なので自分では絶対に使わない仕様なのであり、意識の外だった。

 E - Red and Blue Tree

D 問題を諦めたのに E 問題もコンテスト中の提出は叶わなかった。

やることははっきりしている。できるかどうかは別としてやるだけの問題。

木において辺とは頂点集合を左右に分けるもの」だと前回の ABC に関連して書いたばかり。ある辺について注目したとき、A_i と A_{i+1} が異なる集合に属していれば A_i から A_{i+1} への移動に際してこの辺を通るのでカウント +1。

A_1 から A_2,..., A_m へと移動するとき辺ごとに何回通るかが数えられたら、今度は R−B=K を満たすような塗り方(辺の選び方)を数える。一度も通らない辺は青でも赤でもどっちでもいいので除外してあとで掛け合わせる。

 提出 #26470241 (WA×4 / TLE×3 / AC×37) 昨晩の提出

原因は想像だけど、頂点集合の管理に 1000 ビットのビットフラグ×1000 を使って TLE。後半で謎の DP をして WA。

 提出 #26494106 (AC / 1050 Byte / 183 ms)

頂点集合の管理に UnionFind を使うことを思い出した。後半の DP はシンプルになって、辺を赤く塗った場合と塗らなかった場合を一緒くたにハッシュ表に詰め込むだけ。

1050 Byte は書き過ぎかなと思ったけど、他の Ruby での提出も軒並み 1001 Byte, 2162 Byte, 1019 Byte, 1776 Byte だったから別に突出してはいなかった。

 F - Expensive Expense

前々回の ABC で見たのでこれを全方位木 DP と呼ぶことを知っている。そのときの経験でとりあえず根をとっかかりにすればいいことも知っている>20210928p01.01。根っこの特殊性は親がないことで、(子から親へ処理を積み上げているにも関わらず)親を参照しなければ求められない答えも、根に限れば求まる。そうすれば根を親に持つ子の答えも求まる。以下同様。

木 DP は処理の流れさえできあがれば、葉という一番単純で考えやすいところから差分で処理を積み上げていくと答えになるのでやりやすい。差分の部分の式が難しいことがあるし(20210909)、子のマージが難しいこともあるけど。

 提出 #26495540 (RE×5)

順調にジャッジが進んで行っていたのに最後の最後で次の一群のテストケースに阻まれた(06_n_eq_2_00.txt, 06_n_eq_2_01.txt, 06_n_eq_2_02.txt, 06_n_eq_2_03.txt, 06_n_eq_2_04.txt)。AC は近い。あそこが nil になりそうだなーと疑っている部分はある。

 提出 #26495783 (AC / 602 Byte / 1310 ms)

ABC が 12 時間のコンテストなら ABCDEF の6完も不可能ではない!(慰めはいらねーよ)


ゴルファー以外では Ruby で唯一の AC だった tinsep19 さんの提出 #26477920 を読んだ。

どうやら参照すべき日記を間違えていた。先月の 28 日(20210928p01.01)ではなくて、今月の 4 日(20211004p01)を参照すべきだった。全方位木 DP ではなく木の直径を解法とすべきだった。そっちの方が簡潔になるから。

関連問題である ARC022-C「ロミオとジュリエット」のスライドに2通りの解法が書いてあったらしいのをある参加者のブログで読んでいたが、わざわざややこしそうな全方位木 DP で木の直径を求める方法を知りたいとは思わなかったのだった。

知らずに実装していたし、知っていたのに(理解が浅くて)簡潔な手法を選べなかった。

振り返れば E,F がやるだけの問題だったこと、全方位木 DP、木の直径、どれも前回と前々回の ABC を彷彿させるものだった。どちらも復習はばっちりだった。その結果が3完とは泣かせる。


 提出 #26528881 (AC / 587 Byte / 1032 ms)

DFS 2回で直径を求めるバージョン。全方位木 DP バージョンと比べて、思ったより短くはならなかったけど速くはある。何より各ステップが単純でバグらせにくいと思う。

この単純さは、旅費が最大になる目的地の候補を予め2つに絞っているところから来ている。すなわち、直径(の1つ)の両端に位置する2つの街。

仮に木の中心がある辺にあるとしよう。すべての街が辺のあちら側かこちら側かに二分される。どの街をスタート地点に選んだ場合でも、中心を経由して、中心から最も遠く半径分だけ離れた街を目指すのが最も高くつく。中心を経由しないパスについては、中心に寄り道をするパスを想定して比較材料にすると、最も高くつくケースより安くなることがわかる。


2021年10月09日 (土) 「暑いのに動いてない」なくすエアコン、シャープが発売 設定温度に達した後の湿度上昇防ぐ - ITmedia NEWS」■これは、冷やし終わっても止まらず電気を食い熱を生み続けるエアコン。「冷風を送り出すファンの回転数を抑えることで、室温を下げずに平均湿度を従来機に比べ約12%下げる。」 冷やすことが目的ではないけど除湿のために冷房運転をするからこういう制御になるんだけど、結果は室内の温度ムラを生む。除湿運転によりホットスポットができる。そうするとマッチポンプなんだよ。室温がコントロールできていると思うならあとは除湿ではなく送風を頑張るのがいいと思う。実はコントロールできていませんでした(だから暑い)ということが明らかになったりもするだろう。都合のいいことに消費者の多くは室内機のファンの音をエアコンが仕事をしている音だと思っていると思う。■すべての不満は部屋に対してキャパシティに余裕のあるエアコンを選ぶことで解消するのかな、と考えないではない。


2021年10月05日 (火) 自分のおばあちゃんくらいの世代の人が「行かれる」とか「歩かれへん」という言葉を使っていたイメージがある。行かれるは可能以外の用法も考えられるが、歩かれへんは歩けない以外の意味は考えられない。自分世代なら歩けへんと言う。最近こういう記事を見かけたが(「「ら抜き言葉」で抜かれているのは「ら」ではなかった?「目から鱗」「言われてみれば確かに」 - Togetter」)、ら抜きが行き過ぎた可能動詞化だとすれば、これらは可能動詞以前の形態だと考えられる。『ちいさい言語学者の冒険――子どもに学ぶことばの秘密』という本がある(未読だけど)。子供にとって(たぶん日本語学習者にとっても)歩ける、走れるからの推測で食べれると言ってしまうのはある種の必然なのだろう。自分が小学生の頃は食べれるは食べられるの間違いだと目くじらを立てるくらいにはら抜きが使われていたし、だけどら抜きで何が悪いと開き直れるほどには広がっていなかった。普通一度訂正されたら一生守るよね。二度目は死ぞ。自分は本で読んだ言葉、教科書に書かれていた文法を重んじているが、自然言語はプログラミング言語などと違い文法は後付けの解釈だ。言葉も教科書も移り変わり正解はひとつではない。ATOK では送り仮名を必ず本則に設定するのだが、それ以外が間違いというわけではない。ら抜きは拙く聞こえるしダサいので絶対使わへんけどな! たとえ風俗を写し取るためだったとしても、本の中で使われていたら読み進めるのは苦痛だ。だけど自分から見て上の世代の人がら抜き言葉を話すのもまた、テレビで垣間見える世間で当たり前の光景なんだよね。口語はすっかりら抜きに席巻されてしまった。自分を中心に置いて上と下を見ると時代の流れがわからなくなる。聴覚優位で下の世代の人間はら抜きしか聞こえてこないのでら抜きが当然。では上の世代の人間は? 自ら変化していった? 変化に取り残される人間もいる。省略と拙速は「お嬢様」には似つかわしくありませんことよ。でもさ入れは嫌かも。


2021年10月04日 (月)

最終更新: 2021-11-04T10:11+0900

[AtCoder] AtCoder Beginner Contest 221/F 問題 Diameter set

精進ですよ。おとといあった ABC の F 問題。

コンテスト時間中は木の中心についての理解がぼんやりで解答に至らなかった。そもそも木の中心などというものを考えたことがなく、でもサンプルの2のような円形の木で制限時間内に答えを数えきるためには、木の中心を中心とした組み合わせを考えるしかなかった。

木の直径については知っている。過去にある問題で満点解答のためのヒューリスティクスとして、深さを求める関数を2度呼び出して答えとしたことがあった。後にそれが運任せではなく確かな手段らしいことを知った。証明は知らない。

木において辺とは頂点集合を左右に分けるものだということを知っている。どの辺でもいいので1本選んで真ん中に横向きに置いて形を整えるとアレイ(亜鈴)型になるイメージ。コンテスト中には思い出せなかった。そのせいで問題の木を具体的にイメージする力が弱かった。このことは今朝のトイレで考えるでもなくふと思い浮かんだ。

たとえば直径が偶数の時、直径の中心には頂点が1つある。問題は直径を与える頂点ペアが複数あるときに中心が複数あるかどうか。中心が仮に2つあるなら、2つの中心の中点が本来の中心であるべきであり、直径だと思っていたもの、2つの中心だと思っていたものは直径でも中心でもなかったことになる。だから中心は1つ。

たとえば直径が奇数の時。直径の中心には1本の辺がある。この辺は中心に位置する唯一の辺だろうか。仮に直径の中心に位置する辺が2つあるなら、2つの辺の中点が本来の……(略)。だから中心は1つ。

色の塗り方だけど、中心から最も遠く半径と同じだけ離れている点の集合を、中心から直接出るどの頂点の先にあるかで分類する(中心が辺なら辺が結ぶ2つの頂点を考える)。同じ頂点の先にぶら下がっている2点を同時に塗ってしまうと、そのあいだの距離は必ず直径よりも短くなる。直径より長くなることはありえないし、仮に直径と等しくなることがあるなら、真の中心はどこだ?という話になる。そんなものはない。

ここまでを今朝のうちに納得してから実装したのに、直径が偶数のケースで中心の求め方を間違えたり(提出 #26352819)、線形時間の集計を繰り返して TLE になったり(提出 #26353052)、無駄に長さ N の配列確保を繰り返して TLE になったり(提出 #26353383)、いっぱい間違えた。

配列アクセスとハッシュ表アクセスだと配列の方が断然速いのだけど、初期化が1度で済まないなら、ハッシュ表の初期化コストの低さが効いてくるみたい。

 提出 #26353562 (AC / 750 Byte / 725 ms)

8問目の黄 diff AC。これより上は橙が1問だけだから、かなりのレア度なんだ。

ちなみに 水 diff だった E 問題 LEQ は、まだ TLE を回避する計算方法がわかっていない。

 木の直径

さっき書いた。

木の直径については知っている。(中略)。証明は知らない。

木の中心を念頭に置いて考えると直径を求めるアルゴリズムはこういうことだ。中心は、頂点の場合も辺の場合もあるけど、頂点集合を左右のどちらか(もしくは中心から直接出る辺のどれか)に振り分ける。任意の1点を始点に選んで最も遠い頂点を求める1回目の探索は、中心を挟んで異なる側にある、中心から最も遠い点を求めている。2回目の探索も同じ。同じ側に属する点が最遠点として選ばれることがないのは、中心に寄り道するパスを想定して比較材料にするとわかる。2回の探索で見つかったどちらの頂点も中心からは半径分だけ離れているから、そしてお互いに異なる側にあるから、合わせて直径になる。

 ある問題

さっき書いた。

木の直径については知っている。過去にある問題で満点解答のためのヒューリスティクスとして、深さを求める関数を2度呼び出して答えとしたことがあった。

エディタのログから「.max_by」を GREP したら該当するファイルが(たったの) 15 見つかったので、順番に調べてみた。「ある問題」とは ARC022-C「ロミオとジュリエット」だった。これが青 diff なんだからチョロい!(嘘です。調子乗りました)


2021年10月01日 (金) [AtCoder] 精進。ABC104-C「All Green」(水 diff)。配点の高い問題から解いていきたいけど完答ボーナスがちょっとした番狂わせを演出するのにどう対処するか。貪欲がダメなら DP すればいいじゃない。だから DFS で解いた>提出 #26249595。問題群を ボーナス込みの得点合計÷問題数 でソートして効率のいい方から総当たり(打ち切りあり。残りスコアの合計からと得点効率から計算する最小値更新の見込みから)。問題の解き方は問題群単位で全部解くか1問も解かないかスコアを満たせるだけ解くかの3パターンしかない。制約がたいへん控えめなので解法もバラエティに富んでいる印象>Ruby によるすべての提出。と思ったらほとんどがビット全探索で DFS が少々、といった感じ。DP はダメだったか。たしかに、全部解くか残スコア分だけ解くかの2択に対して p 回繰り返すループは、それが無理な制約ではないにしても無駄が多い。