与えられるテスト結果が誤っており上記の条件を満たす組み合わせが存在しない場合もあります。その場合は 0 通りと解答してください」の意味がよくわからなかった。信頼できない入力が混じっていて、それを見抜いて何らかの対処が求められているのかと思った。実際は単純に矛盾するテストによって答えが0通りの場合があるよということを言っているだけだった。たしかに、複数のテストを照合した結果矛盾が生じて答えが存在しない場合、じゃあ、個々のテストにおいて「開いた(開くはずがない)」「開かなかった(開かないはずがない)」という結果をどう扱えばいいかという疑問が生じる。それは誤りであるという断り書きだった。■D 問題「Masked Popcount」。かつてよく見られた数え上げ問題。苦手だよ。でも精進によってなんとなくの型みたいなものは持っている。上限が N で与えられてるのがやっかいなんだよね。たとえば N=2**60 ならこれは主客転倒で寄与を簡単に数えられる。じゃあどのようにしてそういう状況を作るかというと、与えられた N のビットのうち、最も左にあって最初に倒されるビットを決め打つことによって N を複数の区間に分割する。N のプリフィックスと自由に {0,1} が選べるサフィックスとで構成される区間について問題を解く。たとえば N=0b10101 ならプリフィックスは1のビットの数と同じ次の3つ(0b10100、0b100xx、0b0xxxx)。 もちろん1つもビットを倒さない場合も忘れない。■E 問題「Max/Min」。よくわからない問題。N の上限は 20 万。これは O(N) もしくは O(NlogN) が解法となるよくある制約で、N の制約としてはほぼ上限。そして A が取り得る値が 100 万以下。これは N と比較すると大きい数字だけど、処理すべき区間が 100 万以下であってそれ以上は考えなくてもいいと思うと、そんなに大きい数字には思えない不思議。結局これは調和級数の上限がどうのという問題だったんだろうか。特別な道具を使わずに配列を操作すると決めたあとでも答えを合わせるのに苦労して、結局1時間近くかけてしまった。やるべきことはひと目でわかるんですよ。すべての組み合わせについて足し合わせるに当たって、予めソートして Ai と Aj の大小関係を固定してしまえば考えるべきは分子が分母の1倍か2倍か3倍か……。時間が厳しいので(そこが問われている問題なので)同じ値はまとめて計算しますよ。そうするとどんな入力が嫌かというと、小さい数字が順番に並んでると、処理をまとめることができず、区間を大きくスキップして処理することもできないのが困るなと。だけどね、100 番目、1000 番目の値はすでに十分大きいわけです。100 万割る 100 は1万だし、100 万割る 1000 は 1000 なので、個々の処理量は無に等しい。もちろん1万が1万集まれば馬鹿にならないんだけど、全体で見ても処理可能な範囲に収まることは知っている。名前は出て来なかったけど高3の演習問題でいつの間にか不等号を証明させられていたような気がする。■F 問題「Distance Component Size Query」。解けなかった。セグメント木とか平方分割とか、うまく区間を分割して効率良く数えられないかと考えたけど、できそうでできなかった。でもできるのかなと思ってる。自分にはできないけど。K=0 と K=1、それと K がすごく大きいときでだいぶ様子が違ってくるなとも思った。でも、じゃあ K=1 の場合に限れば問題が解けるかというと、そうでもなかった。■■■D 問題。桁ごとにサイクルを考えるという解法を仕入れてきた☞。LSB から見ていくと、サイクルは2、4、8と倍々になっていく。その中身だけど、前半が0で後半が1という単純なもの。だから 0 から N までの N+1 個の数をサイクルと半端に分けて1の数を数えるのも簡単。提出 #54162917 (AC / 165 Byte / 42 ms)。この考え方だとずるいぐらいに実装が簡単だった。自分は自分の考え方で実装したとき式を間違えて1回 WA を出してるからね>提出 #54107427 (WA)。
w
を足し、それまでに採用されていたが不要になった辺のコストを w+1..10
の範囲で探して引くというもの。単純明快でしょ。しかも、コスト1の辺を使うグラフ、コスト1から2の辺を使うグラフ、……という 10 種類のグラフを維持管理する処理と、辺を追加したことで不要になった辺のコストを特定する処理が一体となっているところが美しいと思うんだ。これは解答というより問題の力だとは思うけども。■■■E 問題。kotatsugame さんの動画(【競技プログラミング】ABC355【実況】)でグラフとしての見かたを知ったので、新規に実装して提出した>#53993734 (AC / 1045 Byte / 402 ms)。変数の取り違えで RE を出し、off-by-one エラーで WA を出し、デバッグ版を提出して WA を出したあとでの AC だった。やっぱり難しいね、この問題。ミスのしやすさも問題の難しさのうちだとすればね。やっていることは辺が陽に与えられないだけの BFS。グリッド問題みたいなもんだ。■C 問題。行や列をスキャンする O(N^2) 解が 455 ms (#53855978) だったところ、カウンターを使う O(T) 解が 132 ms (#53993977) だった。制約がちょっと変則的で、1≤T≤min(N^2,200000)
かつ 2≤N≤2000
なので、T の上限が 20 万なのに対して N^2 の上限が 400 万。N^2 解を許容するためにあえて制約の桁を減らしていることが窺える。C 問題だしね。まんまと甘えさせてもらいました。answer += a*A.size+A.sum while a = A.shift
なんだけど、2要素の和が 10^8 を超えるときは 10^8 を引かなければいけない。A 数列の順番には意味がないので、予め A 数列をソートしておいて、何個の要素が 10^8 を超えるかを効率良く数えられるようにする。二分探索なら log が付くけど間に合うし、尺取りっぽい操作をするなら log は付かない。あっ。自分の提出 #53343786 で 10**8 に付けた名前が P(rime number) というのは嘘だ。■D 問題「Another Sigma Problem」。今度は2要素を文字列として連結してから数値として評価をする。順番に意味があるので並べ替えてはいけない。右側にある要素について、数の和と、下駄の和がわかればいい。下駄というのは、3桁の数字であれば 1000、4桁の数字であれば 10000 を計上する。これを全要素について数え上げておいて、更新しながら答えの計算に利用する。■E 問題「Yet Another Sigma Problem」。かかった時間だけ見れば C 問題と同じなんだけど、配点通り CD より難しかったと思う。自分は Ruby の記述力におんぶにだっこで効率の悪い解答を書いた。まず、N 個の要素について、先頭の文字を見ます。同じ文字だったもの同士をグループにして再帰的に次の文字を見ます。その過程で、グループの大きさを見ます。大きさが Z なら、作れるペアの数(Z*(Z-1)/2)がそのまま答えに寄与します。■F 問題「Tile Distance」。わかりやすい図が付いていてたいへん助かります。K=1 は簡単。マンハッタン距離が答え。それ以外はどうしましょう。最初はフラクタル的に考えてみようとした。スタート地点とゴール地点が隣接していると見なせるまで K を定数倍してみる。で、視点をズームインしながら移動コストを足していけないかと。できなかった。次は大きいタイルと大きいタイルのあいだの移動コストを数えようとした。横方向の移動コストだけを考えてみる。K が大きいなら、上下にあって頂点で接している大きいタイルを経由することで、必ずコスト4で真右にある大きいタイルへ移動できる。K=1 のケースはさっき除外した。K=2 の場合は小さいタイルをそのまま突っ切って移動する方がコスト3なので安い。そういう計算をしているうちに、斜めの移動コストが特に安いと気がついた。K マスを1と数える大きいタイルの座標系で考えてるよ。例えば右に2、下に2の位置にある大きいタイルに移動したいとする。右に移動してから下に移動するなら、さっき計算したコストを使って 3+3 (K=2 のとき) か 4+4 (K>2 のとき) になるけど、ありがたい図を使って数えてみると、たったの4で右下の右下にある大きいタイルに移動できることがわかる。というわけで、まずは斜めに移動して、それから縦もしくは横に移動すると考えると、最適な大きいタイル間の移動コストが求められる。あとは1または4通りと、1または4通りの総当たり(1x1 または 1x4 または 4x1 または 4x4 通り)でほぼ答えが出る。1と4が何かというと、スタート地点ゴール地点から最寄りの大きいタイルの数。「ほぼ」が何かっていうと、スタートとゴールが小さいマスにあってすぐ近くにある場合。これは K=1 の場合のマンハッタン距離と同じだから、提出 #53370051 では K=1 の場合を分けるのをやめて雑に一体化した。全部混ぜ混ぜして最小値を取れば自ずと答えが出てくる。■G 問題「Merchant Takahashi」。今日の G 問題は F 問題と配点が同じ。F 問題を通してから G 問題を読んで、することがなくなったので順位表を見たら F より G の方が通されていたね。自分は G 問題がさっぱりだったけど、データ構造で一発なぐるだけの典型だと仮定して眺めてみれば、移動コストによる報酬の減殺が組み込めるなら、セグメント木が使いたい形だと思った。もちろん組み込めないのだけど。■Highest を更新したけど、明日の ARC の参加費は何レートかな。何レート吸われるんだろう。3-4-5-(略) という配点は水色の自分向けド真ん中なので出ないわけにはいかないんだよな。■■■精進。G 問題。セグメント木を2つ持つんだと2か所で読んだ。それでどうやって距離による減殺を組み込めるかを考えた。左右の端、頂点1と頂点 N にいると仮定したときの所持金のポテンシャルをセグメント木の各要素の値とすると、イーブンな条件で大小比較ができて最大値が取り出せる。提出 #53408933 (AC / 849 Byte / 1999 ms)。2秒制限で 1999 ms は神がかっている。だめだったらセグメント木にブロックを渡す代わりに max メソッドの呼び出しを直に埋め込むだけ(手動インライン展開)。■実際の値の代わりにイコール条件のポテンシャルを扱うのは ARC120-C Swaps 2 でやったことがある。20211022p01。なんでそんな名前で呼んでるのかは自分でもわからない。なんとなくふさわしいような気がしただけ。10101
で表せる。全部で5人なら、これは 1-3-5 位しか考えられないが、全部で 10 人なら 2-4-6 位もありうる。しかし全部で 10 人いて他に 101011111
というピースがあるなら、やはり 1-3-5 位しか考えられない(※ MSB を1位としました)。こうなってくるととりあえず全部を試してみるしか答えを出す方法はないよね、という気持ちになる。1階層ごとに特定の1ピースを埋める DFS をやるんだけど、DFS の呼び出し経路には関心がない。どういうことか。1階層目と2階層目が受け持つピースがどちらも 1
と 1
という形だったとする。3階層目のピースにとって関心があるのは、そして結果に影響するのは、どのビットが空いているかということだけなので、同じ2ビットのどちらとどちらを1ピース目と2ピース目が埋めているかという情報には関心がない。むしろ積極的にその区別を捨てて結果の使い回しをしたい。メモ化です。というわけで、前半ではピースの形を確定する UnionFind を、後半ではピースを N ビットに隙間なく埋めるメモ化再帰をする2段構えの問題だった。要素技術は F 問題までで身につけているはずで、組み合わさったことで F 問題だったのかなと。G 問題ではないなと。ならこれも実装問題だったのか。突破できませんでした。腕力が足りない。■他所で見かけたのでここでも書くんだけど、置きにくいピース(※)から置くような小細工を WA だった最初の提出から行っている。これをやらなくても TLE にはならないだろうけど、とにかく答えを見つければいいという DFS の場合、優先順位を付けて早期に判定ができると劇的に実行時間が縮まることがあって気持ちがいいので、とりあえずやってみて沼にはまるのがいいと思う。その先にヒューリスティック沼があるかは知らない。この問題では、再帰を深く潜ってからやっぱりだめでしたーと判定されていたかもしれないものが、比較的浅い階層で無理だと判断できるようになる効果があったと思う。※置きにくいピースを短絡的にビット長の長いものとしたけど、ポップカウントもいい指標になりそうだと気がついた。1000001
と 1111
のどちらが置きにくいかという話。自分が明確に誤っていたのは、ビット長が同じでポップカウントが異なる 110001
と 101111
の比較で、単純に数値比較で大きい方(最初の方)を置きにくいと判断したところ。小細工だしフレイバーなので許される手抜きだとは思うけど。