ABC133

新ABCで初めての、自分がrated対象外になった回。Fを解いている途中36.8、終了後36.9((解けそうになかったのに)上がるの!?)。rated回と比べて心拍数が上がりにくいと感じた(下がりやすいと言ったほうがいいかな)。体温は上がっていたので、このくらいがちょうどいいという気もする。立ち回りは決めていなかったが、「スピード重視、順位表は見ない」とした。結果的には5完早解き回だった。パフォーマンスは1844相当。調子は悪くなかったが、早解き回だとこんなものだろう。

A - T or T

これは書くだけ。久しぶりに問題文がすぐ見れたので、気持ちよく提出した。

現実にありそうな問題で想像しやすい。いい感じのAだと思う。

B - Good Distance

やるだけとはいえかなり重い。なんとかスピードを出してきれいに書くが、平方数判定で少し迷った。sqrt()の結果が整数のときは必ず小数部がゼロになってるという知識を使い何が最も自然な書き方か考えたがわからなかった。整数にキャストして2乗してみるのもあったかなあ。いやないか。

確かにループが書ければ解けるけど、その中ではかなり難しくなっていて、バランスのいいBだ。

C - Remainder Minimization 2019

ちょっと悩んでしまったが、区間の長さが2019以上なら0が含まれるので0。そうでなければ全探索できる。しかし、i,jが10^9に達することがあると知っていたのに忘れてオーバーフローでWA。問題が面白かったので勢い余ってしまったね。これいつも通り見直してたら気づいたかなあ。気づいたか知る方法ないの嫌だなあ。ていうか今見たら2*10^9だった(これは気づいてなかった)。

これもABCのCって感じで好き。先週のと違って歯応えもある。

D - Rain Flows into Dams

問題文を読むのに3分かかる。そして降った雨の量が与えられると誤読。「解が一意に定まる」と書いてある理由がわからないままとりあえず実装してみるが、なぜこんなに簡単なのかわからないし、Nが奇数である理由もわからない。当然サンプルも合わないので、そこをしばらく考えて誤読に気づく。これは、時間をかけて考えたいやつだ。コンテスト中に短時間で解くのが苦手なタイプの問題。とりあえず山が3つとして考える。降雨量の半分をA(Aにしてるあたり混乱を引きずっている)として、Aが決まりそうな方程式が立つなと、けっこうな時間紙で計算して気づく。2つだと確かに定まらないな、というのも確認。そうして山_N-1に降った雨の量がわかって、残りも芋づるにと思ったけどサンプルが合わない。最初に山とダムを取り違えていたので間違えてる。山_1が求まってるんだよね。2を掛けるとか割るとかも混乱してて、幸い問題が思ったより簡単だったのできれいに書いてACすることができた。ただ、このプログラムが本当に負の数を出力しないのかは確信できてなかった。

E - Virus Tree 2

距離2なので、各頂点から1歩で行ける範囲は全部違う色と言い換える。根から塗っていく方針は思いつく。その塗って行き方がなかなか定まらなかった。そして何とか書いてもサンプルは合わない。細かいミスもしていたが、親の親とも違う色である必要があることが抜けていた(サンプルの図を見ながら脳内でコードを動かし気づいた)。コンビネーションじゃなく順列っぽいやつなのも珍しくて、まあ考えればすぐわかるんだけど、過学習してるなあという感じ。ずっと見通しがなくて、時間がかかった。ここはもう少し速く解きたかった。

F - Colorful Tree

サブセット問題を考える。クエリの色が1通りだったら?難しそうだが、LCAを色毎にやればと思いつく。当然それは計算量的にできない。次に、木がまっすぐだったらと考える。元の長さが全部1で、全部のクエリで2になるとすると、長さNの数列の指定区間にxが何個あるかのクエリに答える問題になる。これは解けそうに見えないし、いくら考えても解けそうにない。ここで、各色に対し高々1回しかクエリが来ないことを考える。すると、色毎に頂点リストを持てばその色の各頂点が区間に入るかは判定できるし、全ての色についてそれをやってもO(N)の計算量で済む。LCAでもlogが付く程度でできるんじゃ?現実には、たくさんの頂点を持つ色のクエリがたくさん来る可能性がある(厳しすぎる何だこの問題は)。何とかできないか考えていたが、どうにもならない。

最後の10分は、久しぶりに「座ってるだけ」だった。ABCでこれかあ。

AtCoderで黄色になるまでにやったこと

AtCoderを始めて2年5か月くらいで黄色になった。

AtCoderを始めるまで

プログラミング歴はとても長くC++が好き、数学も難しいことはわからんけど適当でいいならできるという感じで、相当にAtCoder向きな能力セットを持っていた。

AtCoderの存在はけっこう前から知っていて、一度はやりたいと思っていた。直近のABCを解いたうえで、2017/1/28のABC053に初参加。思いがけず問題の質が高く、継続してコンテストに参加することとなる。

モチベ

健康のために数学っぽいことをしたいと思いつつ何もしない期間が長かった。その意味でAtCoderは渡りに船だった。プログラミングは得意だから障害にならないし、数学パートもけっこういい加減でよくて今の(当時の)自分でも気楽にできる。最初のころは、提出の実行時間や使用メモリがずらずらと表示されているのを見るだけで楽しかった。そのうちレーティングを気にするようになり、悔しがったり落ち込んだり何も感じなかったりしていた。ただ、今の自分がこれだけ感情を動かされることをやっている、それはとてもいいことだと思った。家にいながら緊張したりウンコしたくなったりなるのすごいよね。半年くらい前に気づいたけど体温まで上がるようになってた。たまたま、面白そうな問題があれば解きたいと思い、順位表があれば勝ちたいと思う性格だったので、コンテスト中はそれなりに集中できたのだ(コンテストの時間は100分とか120分とかで、そのくらいなら)(あまり集中できない回もあった)。参加して普段と違う空気の中で考える価値が非常に高いと思っていた。

というわけで、基本的には自分の数学方面の力を維持する(または低下をゆっくりにする)ためにやっている。「レーティングは集中するためのエサに使ってるだけでどうでもいいんですよ」とか表面上は言う。

やったこと

勉強方法

コンテストに出て復習が基本。最初のころは、過去問(ATC001とか)をやって周辺知識をネットで検索する感じで典型知識を増やしていった。1年くらい経つとyukicoderのコンテストにも出るようになった。AtCoderの問題は難易度によらずめちゃくちゃ親切なので、yukicoderで普段と違う筋肉を鍛えたいという意図もあった。なんだかんだ面白い問題あるし、たまに知らないド典型が出る。

復習の具体的な中身は、コンテスト中に挑戦したけど解けなかった1問を解くこと(王道)と、解けた問題を納得行くコードで再提出(納得ポイントは、コードの見た目のきれいさと実行速度)。少し考えて進展がなければすぐ解説を見る(だらだらと考えちゃうこともあるけどあんまよくない)。最近特に心がけていたのは、やりたくないことはやらないということ。それで放置された問題もたくさんあるが、それでいい。苦しみながら進めるほど簡単な道のりではないはずだ(それっぽいことを言う)。

僕個人については、練習によっていまさら地頭が付くはずがないと思っている。しかし、そんな中でも難問を解けるようになるための方法は存在して、それが「簡単な問題を瞬殺できるようになること」。10分かかったことが1分で、1分かかったことが1秒で、1秒かかったことが0秒でできるようになれば、思考の質は変わる。難問を解くための練習は難しくても、簡単な問題を解くスピードを上げる練習ならいくらでも考えようがある(前述の「納得行くコードで再提出」がそれかな(あんま深く考えてなかったけど))。

とにかく、コンテストに参加するのが、練習としてはコスパが良すぎる。最近知ってびっくりしたんだけど、ABC132が終わった時点で参加回数(ratedコンテストのみ)が84回、これがAtCoder全体で40位タイなのだ。参加数上位には日本人しかいない。毎週普通に参加できてるのは思ったよりレアケースだった。

開発環境

普通にVisual Studio Community 2019。ソリューションの中にプロジェクトを2つ入れて、片方でC++を書き、もう片方のサンプルケースでテストする部分はC#で書いた。さらに、Firefox+userChrome.jsで現在表示しているタブのサンプルケースが自動でダウンロードされるようにしてある。これにより、実行ボタンを押すだけでサンプルを実行し全て通ればソースコードをコピーしてFirefoxをアクティブにし提出欄にフォーカスを当てるようになっている(あまり完成度は高くないけど自分で使うぶんにはいいかなくらい)。

サンプルの自動実行は非常に便利。そもそもコンテスト中の時間というのは集中して問題を考えることができる貴重なものであり、たとえ30秒でもサンプルのコピペに毎回使ってしまうのはもったいない。答えが複数ある問題の場合は、サンプルに対する出力が正しいか判定することはできないが、自分のプログラムの出力と出力例を並べて表示できるのでこれも便利。

Visual Studioコードスニペットで、たとえばfor[Tab][Enter]とするとfor文が書ける。これを競プロ向きにカスタマイズした。cin3で3つの入力を受け取るとか、sortでsort(v.begin(), v.end());が出てくるとか。自分は(あくまで競プロerとしてはだけど)タイピングがそれほど速くないので、重宝している。

MSVCとgccでは挙動が異なることがある。たとえば、int64_tの型が違うとか、includeが足りなくても動いてしまうとか。なので、カスタムビルドツールでMinGWgccを使っている。コンパイルオプションは-std=gnu++1y -Wconversion -W -Wall -O0。

ライブラリ

よく使うものだけを挙げる(ただのコピペ元もありますね、コードスニペットにするかの判断は気分)。

素数列挙、最大公約数、1000000007、組合せ、二分探索、UnionFind、グラフ(DFSとか)、ダイクストラ法、BIT(Binary Indexed Tree)、ソートできる構造体、cout << (b ? "Yes" : "No") << endl;、printf("%.8f\n", x);。

他にも、ライブラリにできそうなものは片っ端からしている。最近作ったのはModIntと行列累乗。使用頻度が低いものはいざというときに上手く使えないが、カプセル化の訓練としてはよい。ライブラリの作り方は力の差が出る部分だと思っている。中身をしっかり理解していれば柔軟に使えるし、一方で混乱しているときに無思考で使えることも重要。

コンテスト中の立ち回り

順位表を見るようになった。順位表を見てもそこから下がるだけなので見たくなかったが、少しずつ見るように頑張った。解けそうな問題を知るというのもあるけど、展開を知って油断せず取り組みたいというのが大きい。やはり状況を知っていたほうが楽しめる。

AGCとかでAは通したけどBとCのどちらをやるか迷うという状況のときは、楽しそうなほうを解くことにした。特にそういうことがないなら、交互にやる。「B無理、Cのほうが可能性ある」「いやCは不可能、Bのほうが」と自然に交互になる。時間はかかっても解ける問題を逃さないのが大きいし、時間を有効に使える(順位で不利になってもガツガツ考えたほうが楽しいし実際は順位が不利になる感覚もない)。最近は、最終的に解けない(そして落ち込む)ことはあっても、座ってるだけとか不完全燃焼ということが少なくなった。

考察では紙のノートと鉛筆を使うが、これに加えてソースにコメントで書きなぐるようにもなった。紙に文章を速く書くことができないので、それならPCで書いたほうがいい。編集も楽だし。そのまま提出すれば考察メモがわかりやすい場所に残るのもメリット。文章は、図やソースコードで表現しきれないことを簡単に表現できることがあり、思ったより便利だった。脳内メモリが少ないのでどんどんアイデアを出して、目から供給しながら考える。(もちろん、図で考えたり目を瞑って考えたりもする)(碁石と碁盤を使うことも最近あったな)

あと実装について。自分がC++を書くときはいつも実行速度を重視していた。競プロでは定数倍高速化に意味がないので、あまりこだわると不利になってしまう。ここはけっこう迷う部分だった。最近は、それなりに速度重視で書いて余裕がないときは速度を犠牲にわかりやすく書くという、わりと理想的(略してわりとり)な動きができている。自分のスタイルで書けば力を発揮しやすいし、いつも似たコードを書くことになるのでミスも出にくい。

どんな力が付いたか

自分は元々青程度の力があった。続けることでレーティングはじりじりと向上したが、これはAtCoderに慣れたことによるものだろう。開発環境とライブラリの整備も大きい。それ以外の伸びしろは、自分にはないと思っていたけど、最近ちょっと感じるのは、名前の付いていないたくさんの典型が身に付いてきて自然に応用できてるんじゃないかということ。まあ今黄色になったということは成功が続いているわけで、それによる勘違いかもしれないけど。勘違いだとしても、少なくともプログラミングや数学の力は維持できていると思うし、そう思えているのはまさに望み通りの展開で嬉しく思っている。

ぷよぷよ

さて、2018年3月にぷよぷよを始める(おいうリーグを見てほぼ未経験から始めた)。これにより精進量が減った。AtCoder ProblemsのHeatmapを見るとその時期を境にして明らかにサブミットが減っていた。ただ、毎週のコンテストには出ていたし、7月になってもレーティングへの影響は見られなかった。ここで、1か月以上ratedコンテストがないというかつてない事態が発生する。次のARCで最低パフォーマンスを更新し、レーティングは1994(Highest)から1937まで下がった。そこから半年程度レーティングが低迷する(といっても、それまでより50低い程度だけど)。実力が下がったというよりは、コンテストのやり方がわからなくなったというような心理的なものだったと思う。そもそもAtCoderは練習したところで半年程度で結果に出るような場所じゃないと思っているが、不調に陥ったとき練習量という支えがないと長く引きずりがちなのかなとは思う。

コンテスト前にぷよぷよをやると考察中にぷよぷよの盤面が浮かぶということで、AtCoderがある日の夕食後はやらないようにしてたりも。これも後から考えると行動を縛るデメリットのほうが大きそうだ。それだけぷよぷよに集中していて、それはいいことなんだから。近ごろは目を瞑ってぷよが浮かぶことがなくて寂しい。

2019年6月になり、この頃には状態がかなりよくなっていた。ぷよぷよと競プロをわりと自然に両立できている。元々素養があった競プロと、去年突然始めたぷよぷよ、以前は排他的な関係だったのが、なじんできたのだ。両方好きなのでとてもうれしい。

この項目を書いてたらようかんマッキーが始まった。ブログ書きたいのに勘弁してほしい。強くなりてえな。

なった

6月は、レーティングが1980以上の時間が長かった。つまり、運次第で黄色になれる状態だ。数週間前からこういう記事を書きたいと思っていたので、ほんとに運次第では行けると実感してたんだよ。確率は5割よりずっと低いイメージだったけど。んで、自分の実力が2050あるなら運が悪くてもいつか黄色になれるが、実際は1950程度だと思っているため、この1980以上を維持している好機を逃したくないと思っていた。コンテスト中、物音が気にならないほど集中するとかはできないんだけど、持てる力を集中させて臨んでいた。コンテスト後、明け方まで眠れないことも何回かあった(べつに終了後も興奮している自覚はなくて穏やかな心なのになんか眠れない)。

そしてABC132、全完でワンチャンHighest更新あるかもと思っていたら黄色になっていた。1950以上の状態だとレーティングが上がりにくく下がりやすいことを嫌というほど実感していたので、にわかには信じがたい。rated対象の中ではかなり上位だったのでなんかブーストかかってギリ橙パフォが出たんだね。橙パフォが出ればそりゃあ大幅に上がる。

実感がない。なんか全然嬉しさが湧いてこなくてびっくりした。そうだね、黄色は明日なるんじゃなくて今なるんだね。一生なれない可能性も高いと思ってたけどその時は来た。まあ今はうかれてこんな記事書いてるけど。

黄色になってよかったこと

今の自分は精神ダメージを負いやすく、嫌なことが起きたり懸案事項があったりすると何をやってもパフォーマンスが1段階落ちてしまう。すると、何かあったときそのせいで黄色になるチャンスを逃すんじゃないかとよけい気にしてしまうことがあった。その心配がなくなったことで、嫌なことがあってもあまり気にならなくなった気がしている。嫌は嫌だけど苦痛じゃないというか。お前レーティングを気にしすぎだろって感じだけど、まあ黄色になれる可能性がちょっとあるならどうにかしてその可能性をつかみたいと思うのはわかってもらえるかと。けっこうね、いろんなことをどうでもいいって思えるの大事。

まとめ

僕、将棋ウォーズで初段なんだけど、AtCoderでも初段になれて嬉しい。

ABC132

新ABCのrated回で初めて全完嬉しい!心の調子は悪いと思っていたけど、なんか簡単だった(つまり得意なセットだった)。結局、立ち回りとかじゃなくて問題との相性で決まっちゃうのかね。Eを通して37.1、全完して37.0。すぐ解けたからというのが大きそうだが、わりと落ち着いてやれた。

今回もややトラブルがあった。この前の救済措置で自分のパフォ思ったより落ちたんで、被害にあった人の被害はけっこうでかいのだろうと思う。(「Pythonだけ不利」みたいのは絶対ダメだけど、条件が同じとか運次第とかならratedでいいとは思ってる(得意セットだったとかもぶっちゃけ運だし))

A - Fifty-Fifty

問題が表示されるまでに50秒くらいかかった(ちょっと勘弁してほしい(自分が有利になるとしても))。かなり面倒。p[26]={};しようかと思ったが、さすがに牛刀かなあと思い、ソートして条件を書く。全部同じ文字でもダメなのが難しい。

B - Ordinary Number

簡単じゃんと思い、2番目判定が面倒なことに気づく。別の配列にコピーしてソートした。

C - Divide the Problems

これは考えれば簡単。ソートして真ん中で二つに分ける、つまりd[n/2-1]とd[n/2]を分けることができるか。あの今気づいたんですけど分岐不要でしたか(これはつらい)(d[l - 1] == d[l]とd[l] - d[l - 1]がまとめられることには気づいていてそのまま提出した)(今回は精神的体力のなさを自覚して全体を通し省エネ殺法で臨んでいた)。

理想的なABCのCという感じでかなり好き。

D - Blue and Red Balls

仕切りを入れるやつね。「ほんとに足してnCkになるのか?」って気にしてたけどまあ普通に解いて提出しちゃった。青と赤の両方で仕切りをやって、赤のほうは両端は0個でもいいという。まあ解けたんだけど、背後に何があるとかはわからないね。

E - Hopscotch Addict

最短経路を求めるなら簡単だが、これは長さが3の倍数の経路を求めたい。とりあえず、最短経路を求めて3の倍数なら答えがわかる、というところまではわかる。しばらく考えて、頂点を3倍にすればいいと気づく。何だっけ、けっこう前のchokudaiさんの(個人のではない)ニコ生で頂点を2倍に増やすダイクストラ法をやってて「へーすごい」って思ったのが印象に残ってる。エネルギーを節約するため、BFSではなく、使い慣れたライブラリのダイクストラ法を使う。ていうかこれも3回やる必要なかったですか。対称性から1回でよかったのでは。言い訳すると、s*3からt*3+1へみたいなことも考えてたので。同じ色(言葉にはしてなかったが頭の中では頂点に3種の色が付いていた)のゴールへ行ければいいのね。で、解説読んで気づいたけどDFSでも行けるん!(ん、ちょっと計算量がわからんけど(あれ、アルゴリズムをわかってないのか))

DもEも普通に解いただけのつもりが、相対的にはかなりの早解きになっていた。その余裕から、EとFを落ち着いて考えることができた。

F - Small Products

パッと見簡単そうだけど、状況を把握すると捉えづらい。ただ、これいかにもDPという形をしている。O(N*K)のDPができそうだ。さて、たとえば数列が3で終わってた場合、次の数はN/3以下ならいい。つまり、O(√N)の個数にまとめて考えることができそうだ。しかしここからが大変だ。1から√Nまでと、Nを1から√Nまでの数で割ってできた数を境界とする区間。2種類を扱うからね。DPするのにj以下の通り数にするかjの通り数にするかも迷った。順番に累積やっていくイメージはあったので、「累積要らないんじゃ?」「でもそれだと伝播しない」「じゃあもっと簡単に解けるんじゃ?」という思考をした。しかし、もっとよく考えると累積の向きが逆だ。ここは本当にややこしい。幅が1でない区間区間内のどの数を選んでも同じことなので区間の幅を掛ける。もう思考が無限ループするのだが、なんとか対称性があると信じてきれいに書くことで答えに近づこうとする。サンプルは合わないが、まだ粗削り。0番目の数の左に1があると考えてもよいのでDPの初期化はもっと簡単にできた(コメントアウトして一応残す)(同じことを2回書いたら怪しいと思うべきだなあ)。サンプルが合わないのは√N付近の処理だろうからそこをちゃんと考える。N以下N/2より大きいという区間があり、...、N/(int)√N以下N/(1+(int)√N)より大きいという区間があり、(int)√Nだけからなる区間がある。3つ書いたけど、真ん中のやつ。これN/(int)√Nは(int)√N以上なので最悪でも区間の長さを0にすればいい。N/(1+(int)√N)っていうのが間違いなので(int)√Nだけから成る区間と被らないように調整する(要するに「(int)√Nより大きい」とする)。これでもサンプルは通らない。しばらく考えて、これ累積の向きが別の意味で逆だ。どうしよう。forループを全部逆にするだけでいいのではと気づく。ループの向きを逆にして2つのfor文を入れ替える。サンプルが通った!もう一山ある可能性も考え、すぐ提出。AC。

この内容で全完は本当に嬉しい。Eまでの早解きと、重量級のFをやり遂げたこと。Eの早解きでそれなりの順位が保証されていたため、Fの実装で困っているときにいつものような焦燥感がなかった意味はある。そんな中でも淡々と(当時は進んでいるかもわからない状態だったが)解き進めることができた。結果的に、全完までのスピードもなかなかのものだった。

ABC131

途中で測ったら37.2あって調子良すぎだった(若干空回りを感じたので落ち着いて熱を測った意味もある)。Dまでの早解きでとりあえず人権を得たいのだが、最近のABCはそれを許してくれない。

A - Security

問題が表示されるまでに30秒かかった。「連続したほうが押しにくいのか」と思ったのに実装するころには忘れている。双子回でもないのにYes,Noじゃないとかどうなってんの。あらゆる場所を逆にしてしまい時間がかかった。

B - Bite Eating

「なお、この値は一意に定まることが証明できます」ってビビるよね。頭の中で証明を考えて、0を通過するか0を通過しないかだからOKとか(文字にすると何も言ってないな)。とにかく、全体の和から絶対値が最も小さい要素を引けばいい。絶対値と値そのもの両方を記録する必要があるのが意外だった。int b = abs(b);と書いて気づかなかったりした。これはいつものBより難しい。

C - Anti-Division

こういう包除原理ならできます。ということで区間の倍数の個数を求める問題に帰着させそこを書く。最初、ラムダ式の中でbを変化させてしまった(constキャプチャやコピーキャプチャも考えたが使い慣れず自信なかったんだよなあ時間がないから慣れた方法でやるしかない)。ちょっと、区間を1ずらしたほうがいいのかなあとか思ったけど、なんか対称性のあるコードになったのでまあいいか安全側に倒したむしろ危険な汚いコードで提出。まあ時間かかったといっても10分とかだから、こういう境界を考える問題だと仕方ないところはある。実際は、f(t)ではなくf(t, b+1)-f(t, a)でよかった(なぜ思いつかない!!!!いつもこういう書き方しかしたくなかっただろ!!!卑屈すぎるんだよなあ)。

D - Megalomania

なにやら難しそうな問題。N通りの時刻B_iについて、その時刻までに「締切がその時刻と同じかより前のものが全て完了している」必要があり、逆にそれができていればOK。O(N^2)かと思うが、これはBが小さい順に処理していけば(ソートを除いて)O(N)でできる。この計算量の落とし方は、BITを更新しながら利用する難しい問題で覚えがある。この難しい部分に時間を使わずに済んだのがよかった。

E - Friendships

とりあえずサンプル1を手でチェック。まっすぐに並べてよさそうだと思い、とりあえず最大を考える。難しい。S<X<TでSとTの最短距離が2のケースを考えたが、XがSとTに挟まれてなくていいことに気づいてなかった。とりあえず隣同士は辺でつないで、端から多くなりそうな辺の伸ばし方をしていくが、どうもわからない。Fと並列に考えることにする。Dまでは全部解く前提なのでズガガンと1問ずつやっていくが、ここからはまず両方の問題を見て基本順番にやっていくという感じ。

FをACして戻ってきた。K=0のケースを考えて、完全グラフから辺を除いていく方法を思いつく。しかし、除きすぎると最短距離が3になったり∞になったりする。これを解消できない。二部グラフも思いついた。2つに分けて、うーん、細かい調整が難しい。最初のまっすぐに立ち返ったりもしたが、結局何もわからなかった。細かい調整できそうだけど思いついた方法ではできない(つらい)。

F - Must Be Rectangular!

これは見た瞬間面白そう。y座標が同じペア(というか3つ以上でもいい)を持ってきて、x座標がそのどれかと同じ別の点があったら増える。増え始めたらもう全部埋まるんじゃないかこれは。こう、縦横に連結であることで同値類に分けると、それぞれでx座標の種類数とy座標の種類数と元の点の数がわかればいい。単にグラフにすると辺の数をO(N)にするのが面倒そう。ということでUnionFind(まあグラフと同じことなんだけど2方向で隣同士手をつなぐ)。途中でソートするのでやっぱりインデックスも持たせる。N個の点をいくつかに分けるのだが、最大N個に分かれるし、1個あたり最大でN個含むので、O(N^2)にならないようにするのが案外難しい。uf.root(p[i].i)の値でソートして、長さNをいくつかに分けることにした。rootの値でソートしてrootの値で分けるのだが、現在のrootの値とソート済みの中でのインデックスの両方を保持しておく必要があることに気づかなかった(ここは力不足)。コピペしてsy.insert(p[i].x);とかなってたりしたし、ほんと今日はダメ(2回書くほうが悪いとは思うけど)。頭はよく回ってるけど細部に目が行かない。おおらかすぎる。解けた問題に興味がなくなるのはまあいいことだけど。x,yの絶対値も小さいし、もうちょっと簡単に書きたかったけど、F全体としてはけっこういい動きだったと思う。