/ 最近 .rdf 追記 設定 本棚

脳log[2020-07-14~]



2020年07月14日 (火) 形式的べき級数って聞くと「何それ怖い」しかないんだけど、formal power series って聞くと、「累乗の、連なりの、一般形? う……ん、ちょっとだけならどうにかなるかも」という気がしてくる。錯覚だけどな!


2020年07月13日 (月) fujisan.co.jp を何度か利用しているが、今回初めて良くないふるまいを身につけていると思った。自動継続とメールマガジンのオプションだ。自動継続オプションは購読確定画面とそこまでの経路にはなく、別画面に、デフォルト ON の状態で隠されていた。選ばせる気がない。お知らせ(宣伝)メールのオプションは確定ボタン下方の画面外に配置されていた。購読確定前に選ばせる気がない。下を見て右へ倣えで自ら落ちていくんだろうか。


2020年07月09日 (木) コンテスト時間中に F 問題が解けたことはまだない。時間をかけさえすれば解けるということでもなく、解けた問題だけが日記になっているのだ。

最終更新: 2020-08-15T20:50+0900

[AtCoder] AtCoder Beginner Contest 173F 問題 Intervals on Tree

コンテスト中に解けなかった(問題文を読むところまでいかなかった)問題に挑戦。

「連結成分」っていうのがわかんないよね、まず。出力例1の解説を読むに、頂点集合が辺で繋がれたいくつの部分に分かれるかを数えるみたい。

 考えたこと

  1. 頂点集合は考え得るすべての通し番号から作られる(1~2、1~3、2~7など)。頂点番号と木の構造に関連はない。
  2. 頂点集合ごとに Union-Find するのはどう考えても間に合わない。
  3. LCA が高速に求められたらいけるだろうか。頂点集合に含まれる任意の2頂点をすべて試していたらどう考えても間に合わない。
  4. トポロジカルソートして割り振った番号と実際の頂点番号を見比べて何かわかるだろうか。わかんない。
  5. 木を構成する頂点を「決まったやり方で並べていったら(20200607p01.02)」連結成分が配列の連続する部分として見出せるだろうか。そんなにうまくない。
  6. 木ってどこが根とか決まってたっけ? どのノードでも根になれる? 葉でも根になる? なる。
  7. 一番考えやすいのは、葉が単独で連結成分を構成するとき。葉Cと親Pのあいだに辺があるような(=CとPが共に頂点集合に含まれるような) L,R の選び方が L<=[C,P].min && [C,P].max<=R だから、その否定。(追記:これは嘘。実装中に気がついたがこれだと頂点 C が頂点集合に含まれないケースが紛れている)
  8. 大元の根(※勝手に1つ決めます)に一番近いノードに連結成分を代表させることにすると、あるノードCを根とする連結成分がカウントされるかどうかはノードCと親とのあいだに辺があるかどうかでわかる。
  9. すべての連結成分(=任意のノードを根とする連結成分)について、それがカウントされるような L,R の選び方を数えよう。

 提出 #15106396 (AC / 307 Byte / 351 ms / 35716 KB)

スクリプト化にあたって一番考えたのって、重複組合せの求め方だった。最初 N×N にして間違っていて、仕切りを置く場所を考えるんだったような、と思い出すのに時間がかかった。そして最終的に補集合ではなく目的のものを直接数えられることがわかって無駄になった。

あと、ちょこざいなやり方だとは思うけど、C と P の大小で場合分けをしたくないなと思って符号を利用した>[(c+1)*(p-c),(p-c)*(c-N),].max。あ、カンマが余分。これは「2頂点間の最短パスは短絡辺を通るか通らないかのどちらかである」が最後まで見抜けなかった恨みである。

こうしたら最後の計算で根(c==0)の場合を例外扱いしないで済む。

P[0] = N # 0 を根にする。N は計算のため。
p (0...N).sum{|c|
	p = P[c]
	[(c+1)*(p-c),(p-c)*(c-N)].max
}

 Ruby(2.7)によるすべての提出

ワンライナーとかわけがわかりません><

あ、これ? 「閉路が存在しないならば「連結成分の個数 = 頂点数 - 辺の数」が成り立つ。」 木のどの部分を切り取っても木だろうし、木なら頂点数と辺の数は N 対 N-1 に決まってるので、頂点数と辺の数のずれの大きさがそのまま森を構成する木(連結成分)の数というのは、まあ、言われたらそうかもね、という感じ。

言われなきゃわからないし、なんなら、連結なグラフで頂点数と辺の数の比が N 対 N-1 ならそれは木だというのも、最初からなんだか化かされてるような気がしてる。


2020年07月06日 (月) このツイートに対して今の自分が答えにしているのがこちら


2020年07月03日 (金) 【疑問】車のルームミラーを90°くらい回転させてる人は何なの??? : 乗り物速報」■何度か言及されてるけど、トラックだと飾りなんだよね。教習車は空荷なのでそこまでではなかったけど、信号待ちで後ろについた原付は完全に見えなくなった。そんで左下がりの縦にすると(※実際に確認すると30度も傾いてなかったけども)たぶん助手席の窓から後方をのぞき込むような視界が得られる。不十分だけど足しにはなるので飾りにしておくより役に立つ。歩道を走る自転車が真横を通り過ぎるより先にミラーの中を横切るんですよ、大型車の場合は知らんけど。■うちの親父さんは後ろの車にわずらわされたくないので見えないように歪めてると言っていた。自分はあらゆることを把握したうえで素行の悪い車を無視したいと思っているので賛同できない。それにね、あなたの運転のああいうところとかこういうところとか、独り善がりだと思うんだよね。いつまでも後ろについて走りたくないからさっさと追い越すかルートを変えたくなる。自分自身が他人に合わせられないタチだということを差し引いても相当ですよ。■赤信号が見えるやエンジンブレーキでだらだら減速とか、信号が変わってから殊更マイペースで発進前ルーティーンとか。他にも1つくらい嫌だなと思った癖が強いというか主張が強い運転があったんだけど思い出せない。■周りを気にしすぎてあせったり、煽られて(もしくはそう思い込んで)結果的に周囲を危険にさらすような運転をしてしまうよりよっぽどましだとは思うけどね。


2020年07月01日 (水)

最終更新: 2020-07-09T19:18+0900

[AtCoder] AtCoder Beginner Contest 160D 問題 Line++

コンテスト中に解けなかった問題に再挑戦。(C 問題まで11分で終わらせてそこで力尽きていた。そんだけ時間を余らせてなぜ解けない?)

距離を求めるのに頂点の分類が必要だったのだけど、分類して組み合わせを網羅して距離を計算することができなかった。

今回は頂点を2次元座標に配置することを思い付いて、そうすると組み合わせの網羅や距離の計算が if 文ではなくデータを中心に構成できたので、解答の提出にまで至った。

 提出 #14892703 (AC / 1842 ms / 14560 KB)

N の上限が 2000 だから N×N(=400万)のループは TLE のおそれがあり、実際に 2 秒制限ギリギリだった。提出一覧を見たところ 100 ms は切れないみたいだが 500 ms くらいは普通に切りたい感じ。

  • 距離を求めるのに頂点を3つか4つにクラス分けすれば直接計算できるのではないか
  • クラスに属する頂点は連続する値を持っていて、求める距離も連続する範囲に分布するのではないか

というあたりでもうちょっと。


atcoder.jp/contests/abc16… すべてのiをBFSで最短距離出すところまではすぐ思いついたけど分岐する場所の計算がわからなくて敗北した

BFS とは思いもよらなかった。たぶんグリッドでなくほぼ直線だったからだろう。そういう先入観でプランBが見えなかった。

 提出 #14909026 (AC / 66 ms / 14388 KB)

期待以上に速くなった! 2桁ms!

すでに書いた通り頂点を4つにクラス分けして、始点4クラス×終点4クラスの場合に距離 k の頂点ペアがいくつになるかを計算した。計算は定数時間なので全体で k(=1..N-1) に比例した時間。

L[n][k] が主な道具。n 頂点で直線を作るときに距離 k の頂点ペアがいくつあるかを返す。

C[n][k] は n 頂点の円に対応する。頂点X,Yを除外する-4,-2 がアドホック。

k=1 の場合は例外。他と同じ式に組み込めなかった。


300 ms 台の人の十分に速くてシンプルな提出を見た>#14717011。長さ N の二重ループだった。ありうる2通りの距離のうち短い方を採用するだけだった。これをコンテスト時間中に書きたかったね。まあ、あとからでも書けなかったんだけど。

「2頂点間の最短パスは短絡辺を通るか通らないかのどちらかである」ということが最後まで見抜けなかったからなんだけど、それでも、何らかの方法で答えにたどり着きたかった。


たぶん Python のこの提出(#11387294)が自分と似た方針で同じようなコード構成だと思う。難しくてよくわからんけど。


2020年06月30日 (火) toxic な人


2020年06月29日 (月) スマホが使えなくなった……「世界一受けたい授業」が紹介した「SIMカードロック」でトラブル相次ぐ 専門家は「危険性の高い機能」と指摘【日テレのコメントを追記】 (1/2) - ITmedia NEWS」■スマホに SIM を差し込んですぐに設定した(ゲームと同じくすべての設定を一番最初に見直す一環として)。だいたい月一でそろそろ再起動せーやとお節介なサポートアプリに促されるので、月一で入力することになる。「操作を間違えるとスマホが利用できなくなる、極めて危険性の高い機能」っていうけども、普通に考えて「操作を間違えてスマホが使用できなくなる」のは不届きな他人なのであり、何が問題なのか。もちろんどちらの問題が深刻かは利用者の判断であり、自分が何をしているか、他にどういう手段があるかが覚束ない人のために多様な判断材料とともに情報提供をすべきだったのかもしれない。完全に他人事だけど。■別に SIM が使用不可能になっても「スマホが利用できなくなる」は言い過ぎだろうよ。ケータイネットワークは使えなくてもインターネットもスマホも使える。それこそ無効になった SIM だって代替可能なメディア(媒介物質)に過ぎない(よね?)。スマホを紛失することと比較して特に深刻な事態を引き起こすとは言えない。■「初期設定の PIN を入力します(これから設定する PIN コードではありません)」という注意書きが完全に想像の上を行っている。そうか、そこに罠がある、それが罠になるのか。■「てくろぐ: SIMカードがロックされてしまったら (SIMへのPINコード設定)


2020年06月28日 (日)

最終更新: 2020-07-05T23:55+0900

[AtCoder] AtCoder Beginner Contest 172D 問題 Sum of Divisors

コンテスト全体については順当に、冴えない結果であった。あまり書くことがないので1つだけ。

 D 問題への Ruby による提出(実行時間昇順)

他が概ね 1000 ms ほどかけているところ、1つだけおよそ半分の 515 ms で済ませている>提出 #14757268

ループでは他と同じ式を使ってるんだけど、半分に割って足しているところが鮮やか。足し算の背後にある論理がわかりません。

N/2+1 から N までの数は掛ける2をするだけで N を超えてしまうので、その数自身しか数える必要がない、というあたりかな。ループの中の計算が必要ない。

こんな手の込んだことをしていながら提出時刻も早くて、Ruby の中では5番目なんだよね。一方の自分は、C 問題で脳死の愚直手続きスクリプトを書いていた>提出 #14743690。脳死のまま清書>提出 #14788308。ステートメントを減らそうとしてやりすぎた>提出 #14788890

 脳死といえば……

D 問題にも最初は脳死状態で挑んでいた。こういうスクリプト。

  1. N 要素の配列を 0 で初期化する。
  2. 1..N のそれぞれに対してそれ自身と倍数に対応する配列の要素を +1 する。
  3. 最後にその配列を使って K×f(K) (K=1..N) を計算して合計する。

でもサンプル3が親切にも N の上限値で、このやり方では時間がかかりすぎることに気付かせてくれた。さもなくばずぼらと拙速の代償として TLE を1個拝領していたことだろう。

 [AtCoder 参加感想] 2020/06/27:ABC 172 | maspyのHP

D 問題。問題と格子点の関連がさっぱりわからなかったのだけど、ループでシミュレートしてるΣ計算に N の一般式を与えようとしたときに、Σの中に整数除算があるから、反比例のグラフと軸のあいだの格子点の数に興味があるの? その前に、k*N/k*N/k を約分したり分解したりはできない?

 ABC172D - 西尾泰和のScrapbox

ところで、この縦に足す方法では、半分から先はどうせ1つしかないのにループを回して一つずつ足してしまう。ここを斜めに足せばループは半分で済む。しかしどうせ斜めに足すなら… 左上までしっかり斜めに足す。そうするとループの回数はルートNのオーダーになる。

画像が見えない(scrapbox も CSS を切らないと読めない)。まだ「斜めに足す」がわからない。√N のオーダーになるとは他所でも読んだが、わからなかった。

k*(N/k)*(N/k) の、N/k が1になるものだけを特別扱いするのでなく、2になるもの、3になるもの、4、5……で分けると定数係数としてΣの外に出せる(それとΣの区間も変化する)とか、そういう話なんだろうか。いや、どう書いてあるかはざっと読んだんだけど、読んだだけで解れば世話がないわけで……(あとでスクリプトにして確かめよう)。

 スクリプトにして提出したら 997 ms だったものが 68 ms になった。(比較のため Python だと 32 ms)

しかし明らかにもっとすっきり書く方法がありそうなんだよな、っていうかそれはすでに Python スクリプトとして示されてるんだけど、理解できないのです。

自分がやったのはすでに書いた通り、「N/k が1になるものだけを特別扱いするのでなく、2になるもの、3になるもの、4、5……で分けると定数係数としてΣの外に出せる」ということを利用して、k=1..N のループについて前から計算すると同時にループの反対側にある N/k==k ()となるケースを計算して両端からループを進めようということ。繰り返し回数は 1,2,3,...,N/3,N/2,N の半分になるはずなんだけど、中間地点がどこにあるのか、全長がいくつになるのか、わかりません。

でもまあ、√N のオーダーになってるみたいだから、N=a*a だとして、1,2,...,a-1,a(=N/a),N/(a-1),...,N/2,N なんでしょう。

 整数への切り下げがなければ

m=N/k; n=N/(k-1) なのを利用して 68 ms のスクリプトのループの中の式を s+=N*(N+1)/2; s+=k*m*(m+1) と整理できそうなんだけど、そうするとこれまたどこかで見たような式(と定数)でさらに整理できそうな雰囲気があるんだけど、m と n の関係は通分したり約分したりできる関係ではたぶんないんだよね(答えが合わないから)。

 AtCoder Beginner Contest 172 D - Sum of Divisors - Crieit

図がわかりやすい。オーダーをちょっとずつ改善していく構成が付いて行きやすい。そして最後に見逃せないこれ>「なお、O(N^1/3)の方法もあるらしいです。

格子点のやり方がこのオーダーらしい。さっぱり想像がつかない

じゃあね、せっかくリンクを張って紹介してくれた先(「格子点の数え上げの高速化 - memo」)を読みましょうよ、って話なんだけど、高速化云々より前に格子点がどのように関わってくるのかがまず知りたいよね。

 格子点の数え上げの高速化 - memo

まずここから(わかんない)。

1 から n までの約数の個数の総和(つまり、y=n/x の第一象限内の格子点の個数)は 2 \cdot \left(\sum_{i=1}^{\lfloor{\sqrt{n}}\rfloor} \left\lfloor\frac{n}{i}\right\rfloor\right) - \lfloor{\sqrt{n}}\rfloor^2 などを用いて計算することが多く

  • y=n/x と y=x の交点が (√n,√n)
  • y=n/x は y=x を軸にして対称
  • x=1..√n の範囲で y=n/x と y=x のあいだにある格子点を数えて2倍する? (たぶん y=x 上の格子点を一度だけ数えるよう気を配るのが面倒くさい)
  • x=1..√n の範囲で y=n/x と x 軸のあいだにある格子点を数えて2倍して、重複して数えた (1,1)から(√n,√n)を対角線とする正方形領域の格子点を引く
 「考え方」
  • 曲線上にある格子点が列挙できたら
  • 隣り合う格子点間を結んで斜辺とする各台形内の格子点を計算で求めるだけでいい
  • 斜辺上にある格子点にだけ注意してね(「傾きが既約分数の場合」)
  • 最初のステップの列挙には Stern–Brocot tree が使えるよ
 「求め方」
  • わかりません。ニュートン法みたいな試行を繰り返すの? むしろユークリッドの互除法?

 ループをまたいで式を整理した。

それというのも Python の方には2桁msの提出が1ページ以上もあって、オーダーは変わらないしブレもあるだろうとはいえ、28 ms と 32 ms のスクリプトのあいだには明らかに式の複雑さに差がある。

 Python によるすべての提出(実行時間昇順)

実行時間昇順で並べたときだけ自分の 32 ms の提出がリストされない。降順だったり提出時刻だったりでソートすれば現れる。消えているときは代わりに他の人の 32 ms の提出が2回リストされている。

32 ms の1つは自分のだが、28 ms は例えばこれ>提出 #14788253。平均タイムからして明らかに速い。

未だ及ばずながらだいぶ迫ったのではないか。

 これで最後。

最後に÷4するのにループの中で無駄に×4してるのが気になったので。

これを最適化というのではないか。この問題でしか意味のないループになった。少なくとも自分は式の意味を、途中からは理解していない。

実は最終版の while ループの中身は一番最初の 997 ms の提出とそっくりになっている。戻ってきた。

 Python のこの提出 #14841471

Ruby で書くとこんな感じ。

N = gets.to_i
p (1..Math.sqrt(N)).sum{|k|
	n = N/k
	k*n*(n+1)-k*k*k
}

違いを見比べると -k^3 がループの中にあるか外にあるかの差なんだろう。Wikipedia による と \sum_{i=1}^nk^3 = \left(\frac{n(n+1)}{2}\right)^2 らしい。

k*[n*(n+1)-k*k] からは、何か、意味が読み取れそうな気がするね。数学力があれば見えるんだろうか。数学力があれば意味を保ったまま易々とたどり着けるんだろうか。

 結局これでいい。

ループ後のつじつま合わせの正体が -\sum{k^3} だとわかったので……

N = gets.to_i
s,k,n = 0,1,N
while k<=n
	s += k*n*(n+1)
	k += 1
	n = N/k
end
s -= (k*(k-1)/2)**2
p s

 両辺の k は異なる。右辺の k が 1,2,3,... の順で繰り返される k として、それに対応して左辺を満たす k が N,N-1,N-2 の順で発見される。1対1対応ではない。


2020年06月27日 (土) 大阪大学、ブチギレやん」■マーク・ピーターセンさんの英語について書かれた本で、「○○ University」と「University of ○○」の違いについて触れられていた。いわく、University of Kyoto はアリだが、University of Meiji(?) はナシだという。府立大学と市立大学を統合した大学が University of Osaka を名乗るのは正当性があるように思える(※)。わかりにくいけど。そして OSAKA UNIVERSITY (阪大) の立場とは? もともとわかりにくかったものがわかりにくいというだけの話なんだよなあ。■■■続。「「University of Osaka」が大阪大学の英語名称として使用されている実態 — 大阪大学」 阪大が University of Osaka を名乗っておくべきだったという傍証(茶化しています)。とはいえ、あんまり区別されない実態があるなら名前をかすめ取るような行為を非難するのもむべなるかな。■あとから考えるとタイミングがアウトかもしれない。たぶん University of 地名 は固有名詞としてよりその地の大学の代名詞的意味合いを持ってると思う。あとからやってきてその呼称(呼称です)を固有名詞として使用するのはどうなんでしょうね。商標登録とか紛らわしいインターネットドメインを押さえておくとか、そういう奪い奪われは商売の世界だけにしてほしいな。■よく知らないけど、新しい方はシティカレッジ的な雰囲気がある気がする。大学の実際も「シティカレッジ」という用語の実際も知らないけど。■阪大が1位で、新しい大学は3位のマンモス大学になるらしい。たぶんカレッジではないし、頭数ではどっちも存在感があるな。


2020年06月25日 (木) 韓国の高校で出された日本語の問題です。 問題の下線部分と発音が同じものを選んで下さい。 日本人の皆さん、なめてかかると間違えますよ~」■難しい(笑)。日本語の「ん」は1種類や2種類ではないという話を聞いたことがあるけど、解ったためしがない。「っ」についてはちょっと違いがわかった。「まっすぐ」の「っ」は歯のあいだから息が漏れていた(初めて知った!)。「あさって」と「きって」の「っ」は舌で歯のあいだを埋めて「て」の発声に備えていた(初めて知った!)。「きっぷ」の「っ」は唇を閉じて「ぷ」の発声に備えていた(初めて知った!)。「がっこう」の「っ」は舌の根で口の中の微妙に奥まったところを塞いで「こ」の発声に備えていた(初めて知った!)。でも音としては「まっすぐ」を除いてどれも同じ無だと思うんだけど、そういうもんではないのか。■「っ」の場合をヒントにして、音ではなく口の形で区別することを意識したら3問とも正解だった! 耳は使わない! ていうかその違いに気がつけないのが日本語ネイティブの耳なのか?


2020年06月24日 (水) 読まれるブログフォーマットと読者のリテラシーと好き嫌いの話。https://mobile.twitter.com/chokudai/status/1275308912896405507■個人的にはとってもわかる。「関係ない写真を間に入れまくる、内容のないblog特有のフォーマット」「リテラシー高めのエンジニア向け記事だとマイナスに働く効果のが大きいかなー、と思ってた」■たしかちょっと前に似たようなつぶやきが、それも AtCoder 界隈であった。動画かテキストかという話。自分はこういう派>「わからない。提出 #13147757 (WA)。解説 PDF で考え違いを教えてもらおうと思ったら解説動画しかなくて、うんまあ、じゃあいいや。」 テキストでぱっぱっと要点だけ知りたいし、まだら模様の理解度に合わせて時間を配分したいし、動画はそれができないから時間の無駄だと思ってる。それが言い過ぎだとしても、効率が悪いのはたしか。■過去に何度か書いたけど、自分は画像処理が苦手でアイコンをほとんど識別しないし、現実や画面を通した映像から読み取れることがごく限られているし、テキスト処理に特化している。人間(の集団)が嫌いである種の実写がきついというのがまた別にあるけども。AtCoder の解説動画がどんなものかはまだ確かめたことがないけども。誰々がすごく丁寧な解説をするという評判だけ聞いてるけども。■Web メディアがページを無駄に(※主観です)細かく分割するのも必ずしも広告がらみの理由ではないらしいし、マスにリーチするということは、その過程が見えるということは、とりもなおさずターゲットが自分から離れていくことを意味するのだともはや承知している。逆にターゲットが自分の方に近づいてくるとき、その過程は見えない。まだ自分にリーチしてないからね。だから好き嫌い(※主観)ではあっても善し悪し(※客観)ではないんだろう。■あと「エンジニア」っていってもピンキリで、ピンはごく少数なので……。Web サービスでユーザーが増えると質の平均が下がったと嘆きが聞こえてくるのはあるあるなので。■パソコン通信は知らないインターネット老人会からでした。


2020年06月23日 (火) 免許を更新した。更新手続きの休止期間が長引かなくて余計な面倒がなかったのは幸い。今日に備えて10年来の眼鏡を新しくしていた。ボーダーについてはなぜか 0.6 だと勘違いしていたのだけど、0.5 から 0.8 までいろいろ基準があっても 0.6 はないみたい。眼鏡屋さんが 0.7 だと言うのはしかたがない。自分の事情は自分で知っておく必要がある。いつまでも慣れない深視力は 10.0 mm、5.9 mm、0.9 mm で余裕(20mm3回より悪くなければいい)。■通知ハガキの記述と異なっていたのだけど、まず、4桁の番号は1つだけしか入力しなかった。ハガキには2組と書いてあったけど、1つは免許センターの機械が勝手に決めた。もう1点はハンコがいらなかったこと。用意するものに含まれていたから持っていったけど、そういえば使わなかったなと。

最終更新: 2020-07-09T19:28+0900

[AtCoder] AtCoder Beginner Contest 157D 問題 Friend Suggestions

 成長の記録

WA(Wrong Answer)の記憶なんてないまま新鮮な気持ちで挑戦したら普通に解けた。過去の提出を見直してみたらまあ、解答の構成がびっくりするほど瓜二つ。

では二者の分かれ目はどこに?

 友達の友達ネットワークの把握 (WA の理由)

WA の方はすべての人について一度だけ、その友達リストを処理している。AC した方は深さ優先探索で再帰的に処理している。なぜ再帰が必要か?

ある人 A と B が友達で、また C と D が友達であるとする。この時点で2つの友達グループがある。ここで A と C の両方と友達である E さんを処理するときに、A と C と E を繋ぐだけでは不十分で、すでに A や C とグループを作っていた B と D の所属グループまで更新しなければいけない。これをするためには配列を通り一遍に処理するだけではダメで、友達グループを記録した配列を何度もなめなめするか、再帰的に処理をする必要がある。

今ではこういう処理を Union-Find と呼ぶことを知っているし、グループの大小を管理することで書き換え処理が軽減できることも知っている。検索したらこれは序の口で、まだまだ奥が深いらしい。読んでないよ>「素集合データ構造(Union-Find)」「UnionFindTree に関する知見の諸々 - noshi91のメモ

 例えば ARC097 の D 問題 Equals
自分の提出 #8121358 (AC / 310 Byte / 340 ms / 16252 KB)
何も考えず小さいインデックスにグループを代表させている。
betrue12 さんの提出 #2497462 (AC / 1004 Byte / 315 ms / 15116 KB)
rank(木の高さ)の小さい方を大きい方に接続している。

インタープリタ型言語は基本的に書けば書くほど実行に時間がかかるものだし、一般化して構造化すれば無駄が生じる。多く書いてそれが速いなら、アルゴリズムが優れていることに他ならない。

ところで、つい先月の新しい提出にすごいのがありますね。「Ruby(2.3)によるすべての提出(実行時間昇順)

  • tamura2004 さんの提出 #13758236 (AC / 915 Byte / 291 ms / 12292 KB)

    1. UnionFind クラス唯一のインスタンス変数 @data 配列の初期値は -1 であり、これはすべての要素がサイズ1のグループを作っていることを意味している。def size(a); -@data[find(a)]; end @data ひとつでグループとサイズの両方を記録している。
    2. unite メソッドでは @data[b] = a によって b グループを a グループに併合している。事前の比較により a グループの方が b グループより小さくないことが保証されている。しかし同時に行っている @data[a] += @data[b] の意味がわかりにくい。これは @data のもう一面、大きさを合計している。
    3. find メソッドの再帰停止条件は @data[a] < 0。負になるのはルートに対応する要素の値で、ルートにぶら下がる要素は 0 以上の値で他の要素をポイントしている。

    @data 変数ひとつであれもこれも済まそうなんて、なんてケチで欲張りなんだ。

 必要なのは中身ではなくサイズ (TLE の原因)

コンピュータで処理するものなのだから、現実的制約は無視できない。集合演算と整数の引き算(+α)のコストの差。十分過ぎて必要のない情報にコストをかけてはいけない。

 「成長の記録」とか見出しをつけたけど……

引き合いに出した ARC097 の D 問題の AC 提出は去年の10月のものだった。3月時点ではそれを糧にできていなかったのだな。

さらに言えば ARC097 の D 問題には AC 提出の前に1つ TLE になった提出があったのだけど(#8121130)、TLE の原因がグループを表現するのに集合を使っていたから。3月の提出が TLE なのと同じ理由。まるで成長していない……(それどころか WA まで)。去年の10月は TLE のままで終わらなかったのが偉くて、30分くらいかけてグループの中で一番小さいインデックスにグループを代表させることにしたらしい。それがどうして3月に生きなかったのか……。

しかし今日の日記を書く過程でさらに省メモリかつ高速なスクリプトへの手がかりを見つけられたのはもっけの幸い。わずか2日での進歩である。

tamura2004 さんの提出を参考に。同じ問題に対する#10479576ではなくて、さっき引き合いに出した#13758236の方。

出力形式も変えたけど、ジャッジがスペース区切りと改行区切りを区別しないらしいのは kotatsugame さんの何かの提出で知った。これって kotatsugame さんの記事なんだけど……「AtCoderで実行時間0msを狙う - Qiita

どうしても1ケースだけ1msかかってしまう……せや!テストケース特定したろ!」「ちょっとくらい……探索サボってもバレへんか」「進む方向を定めるのに、ベクトル(sin(r),cos(r)) (r=0,...,99)を使っています。根拠はないです。

「根拠はないです」やあらへんでまったく。ゴルファーでもあるこんな人がジャッジの細かい仕様を知らんはずないんだよなあ。

それに問題を読み直したら「答えを空白区切りで順に出力せよ」と書いてあって、たしかにスペース区切りの出力例は出力形式の一例に過ぎないといえる。

最終更新: 2020-09-01T19:43+0900

[AtCoder] AtCoder Beginner Contest 157E 問題 Simple String Queries

 成長の記録(2回目)

コンテスト本番では問題文を読むところまでたどり着けなかったし、仮に読んでいても TLE は免れなかったろう。

しかし今や蟻本でセグメントツリーについて読んだので何の問題もない。適切なデータ構造を扱えますか、というだけの問題である。それと時間内に実装できますか、という……(BITを使おうとしてた時間を含めて3時間くらいいじくってた)。

提出 #14702134 (AC / 835 Byte / 449 ms / 39004 KB)
セグメントツリーを初めて実装したのがこのとき>20200610p01。今回は更新があったのと、ツリーを根からたどったのと、2の冪乗になるように詰め物をしなかったのが初めてのこと。
提出 #14702414 (AC / 774 Byte / 350 ms / 38728 KB)
最初の提出では本を見ながらそれにならって値を根からたどって取得したけど、そうしなければいけない理由はなかったのだった。以前の雰囲気実装と同じく葉から始めて値を取得するバージョンがこれ。速いよね。fn メソッドをインライン化したらもうちょっと稼げると思う。
提出 #14702882 (AC / 708 Byte / 333 ms / 38948 KB)
fn メソッドをなくしたけどあんまり変わらない。ループの中で多重代入というのが影響することも知ってるけど(20200428p01.08.01)、表記の簡潔さはゆずれない。
ST#[]= メソッドのために1個だけ詰め物をしたけど効果がなかったので(#14702766 RE)、詰め物の意味はない。消し忘れ。
スクリプト言語での popcount は二進文字列化して "1" を数えるものが多いみたいだった。高々26ビットだということがわかっているので log2(32) セット(=5組)のビット演算で済ませる魔法があるらしいし、そういう提出もあった。その他にも色々と『ハッカーのたのしみ』で紹介されている。ループを回してるのは芸がないね。

 Python によるこれらの提出。提出 #14610743提出 #10774518

内部データサイズが単なる 2N に見えるのが不思議。添字の扱い方はヒープに見えるけど、2の冪乗じゃないと階層が崩れて右が左に左が右になりそうなものだ。さっぱりわからん。

蟻本の著者の一人のスライドを見つけた。

  • 実際には,この実装なら n が 2 の累乗でな くても動作する

    値の更新の方はこのままではダメで,同様の 手法で再帰関数で書けば OK

  • ただし,配列サイズは MAX_N * 2 - 1 では 足りない – MAX_N * 4 取れば十分

まだわかりません。それに Python による fold 関数とスライドにある query 関数は引数の数が全然違うんだよね。片方は再帰ですらないし。

 追記@2020-09-01 「Segment Tree のお勉強(1) | maspyのHP

一方の提出ご本人による記事である。「いわゆる非再帰実装」「N = 2^n を仮定しない」 これこれ。ありがたやありがたや。