ABC284

6完。Cまですごい早解きできる。そこからはかなり難しくなったが、結局Fまでの早解き回だった。Gが難しい。

A - Sequence of Strings

普通にforで受け取ってforで逆順出力。32秒でAC。for文書いても簡単なら時間かからないんだな。

B - Multi Test Cases

これもやるだけ。Bまでで2分を切っている。コード量は普通でも内容が簡単なら速い。

C - Count Connected Components

UnionFind。最初連結成分はN個で、辺を追加していってmergeされる毎にデクリメントする。これも楽。

D - Happy New Year 2023

いきなり難しくなる。9*10^18はlong longにギリギリ入る(2^63でググった)。TL3秒でテストケースが10個なのでN^(1/3)くらいの計算回数ならギリギリ間に合うかというイメージ。よく考えると、一番小さな素因数はN^(1/3)未満だ(当時は、pが大きければqが小さいとか場合分けしてた)。そこを全探索。いや、そもそも素数だけやればいい(当時は、pもqも素数だからとか考えてたけど、そもそも素数でない素因数などない)。ということで3*10^6までの素数を列挙しておいて(この操作は1回でいい)、試し割りで最小の素因数を見つける。見つかったら、それでもう1回割れるか確認して、割れたらそのまんま出力、割れなかったらsqrtで平方根を概算し、ずれてたら1ずつずらしていく(1ずれるたびに10^9くらいのオーダーで変わるしdoubleの誤差はそれより小さいのでずれても1個とかのはず)(解説は普通にsqrtだけで草、まあ不思議はない)。

E - Count Simple Paths

何をやったらいいかわからない。手でパスを作ってみる。始点が固定なので、DFSでよさそう(パスがどうやって区別されるかを考えてもそう)。同じパス内で2回同じ頂点を通ってはいけないが、別のパスでは通っていいのでフラグの管理が難しいと思ったけど、入るときに印を付けて出るときに消すという普通のやつじゃん(なんかその変数をグラフの外で持ったりしてるし混乱してた)。長さ0のパスも数えるというのはサンプルで気づいた(そのほうが実装は楽なので気づく機会はありそう)。あとは10^6個になるまで数えるだけ(頂点を訪れるたびにインクリメント)。制約の意味がわからなかったが、提出してAC。

解説を見るとなるほど、自分の実装も10^6回まで頂点を訪れてそのたびに全部の辺を列挙してる。確かに次数10以下の制約なしに間に合う証明はできてなかった。ただ、そんなに訪問済みの頂点ばかり周りにある状況作れるのかな。次数の制約なしで自分のコードをTLEにできるケースが思いつかない。いやあ難しいなあ。

(追記)TLEにできるケースを作れた。色々考えていたが無向グラフなので工夫するのが難しい。だいぶ経ってから、単に完全グラフでTLギリギリになると気づく。まず試せ。次に、サイズ9万のパスグラフとサイズ10のクリークをくっつけて、クリークの(パスグラフとくっついてない)頂点の一つからパスグラフの各頂点へ辺を張る。パスグラフの端からスタートして、「クリークを通り抜ける経路の個数」×「9万」だけカウントしない辺が現れるという仕組み。無向グラフなので探索順によっては距離1でクリークに行けてしまいダメだが、それが後回しになるようにすればよかった。これ探索順を逆にすれば短時間で終わるケースしか作れてないし、そもそも次数が大きい辺が集まっていればその部分グラフだけで10^6は行くし、次数の制約なくても解けそうな気がするけど、まあ全然わからない。

F - ABCBAC

Z-algorithmくらいしか思いつかんけど、反転してるのが嫌らしい。i=0だったら先頭は何も一致しなくていいしな。まあよくあるやり方として文字列を2個つなげてみる。Tを反転してつなげると、iを固定したときの先頭からの一致数はわかる。じゃあつなげ方を逆にしてもう1回やれば残りの一致数もわかるか。結局、iを全探索できる。Z-algorithmを2回やって前計算しておけば、iに対して条件を満たすかO(1)で判定できる。判定できたら、i文字目からのN文字を取り出して反転して出力。

あれっ、自分のコードではi=Nのケースをやり忘れている(解説見て気づいた)。でもそのケースはi=0でもできるからACはできるんだな。

(追記)長さ半分でいいのかよ。Tを半分に切っていいって、見えないなあ。世界が違った。負けです。

G - Only Once

要するに数列Aはいくつかのループとそれに入るひげ(分岐してる可能性もある)みたいなグラフを表している。(0-indexedで考えて)対称性からB_iに0が1個だけ含まれる回数をカウントしてN倍する。最初全く解ける気がしなかった(ループのサイズも個数も構成する要素もバラバラでどうやってもまとまらない)。また、勘違いで自己ループも1回しか登場しない数にカウントしていた(問題が難しすぎるとこういうしょーもない勘違いの頻度も激増する)。やる気も減少していたが、終了15分前くらいになって、ループを構成する頂点を固定したら実際にそうなる確率を単純な形で計算できそうなことに気づいた。行き先を1個ずつ決めていくと、ループの個数とかによらず、次の頂点として可能なものが1個ずつ減っていく(階乗になる)。次に、ループにならないやつは枝分かれもあるので厄介。ただ、頂点0からどこへ行くか場合分けするとできそう。ここらでコンテスト終了。

場合分けして小さい問題に帰着してDPできるかと思ってたけど状態数が2乗になるから無理っぽかった。そうじゃなくて普通に場合分けを進めていくとこっちも前計算できそうな形になる。これで解けるかと思いきや、Mが素数と限らないのを忘れていて破滅。

(追記)kyopro_friendsさんの解説通りにやった。こんなシンプルに書けるのか。自分が考えてた方法は詰めることができなかった。疲弊した。