2024年9月25日水曜日

日立ヴァンタラプログラミングコンテスト2024(AtCoder Beginner Contest 368)

  Eが解けなかった。

コンテスト後のツイート

E - Train Delay

 解説・解説放送を見てACしたが、ACした後も自分のコードが正しいか自信が持てず乱択で比較したりしてしまった。(正しかったよう)

 出発時刻が早い列車から見ていこうというのは良いが、それ以外に何を持てば良いのかが難しい。
 各駅ごとに、(本来の到着時刻, 遅れたときの到着時刻)を全て持ってそれら全てについて調べればもちろん良いのだがそれでは計算量が間に合わない。

 ある列車について、その発車時刻より以前に本来の到着時刻があった列車全てについて調べ、遅延に原因する列車が特定できたとき、(調べた範囲の他の列車については忘れて)その列車についてのみ覚えておけば良い。ここが重要。

 なぜそれで良いのかが整理できなかったのだが、それ以降の発車時刻のものは、今回遅延原因だった列車とは本来乗り換え可能だったから、ということ。

 ここで迷う人は結構いるんじゃないかと思う。ただ、自分が図を描いたり色々してもしばらく理解できなかったことをこうやって言葉だけで書いても伝わらないとは思うけれど……。


2024年9月21日土曜日

yukicoder contest 447 オムニバス

 ABの二完。Bでxorの基底をちゃんと使えたのは良かったが、Cできなかったのはひどい。


No.2896 Monotonic Prime Factors

 解説AC。

 コンテスト中、素因数をソートして横に並べて、それらをn個に分ける場合の数だから重複組み合わせでできるはず……と思いながら、答えが合わず通せなかった。
 x個をn個に分けるのは、x個の間x-1個のうちからn-1個を選ぶのだから、x-1Cn-1になる。これが考えても出てこなかった。

 出てこないにしても、重複組み合わせというキーワードが思い出せているなら、検索とかでどうにかなったはずだよね。ちゃんとしよう。

No.2897 2集合間距離

 解説AC。

 この制約ならBFSするだけと気付かなかったのも反省。また、tester解のように45度回転させて平面走査みたいなことを考えていたのに、二分探索を思いつかなかったのも反省。

2024年9月18日水曜日

RECRUIT 日本橋ハーフマラソン 2024夏(AtCoder Heuristic Contest 036)

 pretest71位→システムテストリジャッジ前100位→リジャッジ後72位でした。

 リジャッジありがとうございます!
 システムテスト結果が出たとき、ジャッジおかしいんじゃないの? というようなことをツイートしたけど、それほど確信はなかった。リジャッジでTLEが減り、順位が上がって良かった。

 最終日まで300位前後で、最終日にAの別の作り方を試し、順位が大きく上がったのも嬉しかった。

 大体ツイートのまとめです。

コンテスト後のツイート

前半四日くらい

 Aを固定したとき、「Bの変更は全体取り換えのみ」とすれば最適解を求められそう……と思って書くが、色々バグらせた上PyPyじゃ間に合わない(あとでRUSTで書き換え、枝狩りをして一応間に合う。でもシステムテストだとTLEするかも→した)

最終日まで

 Aとしては適当な評価値で山登り/焼きなましを考えていたが、それだと300位前後にしかならない。最終日に方針転換を迫られる。

最終日

 t[i]からt[i+1]への最短距離での行き方を最初に調べ、それらを連続部分列として多くもつようにしたい。

 まず、各点から各点までのBFSを使って、t[i]からt[i+1]まで進む頂点を列挙。この集合をNEEDとする
 そのうち、長さが短いもの同士で、一番最初の文字=一番最後の文字とかなったりするものを組み合わせていく。評価値は、「できあがったものの長さ/組み合わせた個数」のようにする。
 評価値の小さい順にAにおいていく。ただし、Aの長さ+Aに出てきていないNEEDの個数<=LAとなるギリギリまでおき、その後はNEEDに入っているものをおいていく。
 NEEDに入っているものxをおくときは、今までのAの中で、xのそばにxと隣接するものができるだけ多いような場所に置いた。

 これが最終提出でした。

気になっていたこと

 ところで、「自分のコードは、Aを与えられたら、Bを全取り換えするものだけ使うなら最善のものを出力しているつもりだったけど本当か?」が気になっていたので調べてみた。

 上位の方のコードで出力しているAを入力し、そのときのスコア(そのコードでのスコア vs 自分のスコア)を比較すると、

・B全取り換えだけでやっている人には大体ちょっと勝ち(or引き分け)
・部分取り換えも使っている人には結構負け

 という感じだった。

というわけで、自分のコードは、B全取り換えだけ使うなら多分最善だったけれど、

・厳密な最善を求める必要はない。多少スコアを犠牲にしても、計算量改善した方がメリットは大きい
・部分取り換えの使い道は少ないと思っていたけど、自分が思ったいたより大きく改善するらしい。

と分かった。

 Aを決める部分の方がスコアに影響する度合いとしては大きいけど、そっちは発想力が求められた印象。今回はたとえば、「中心を決めて木構造にする」というAの取り方が強かったようだけど、コンテスト中に思いつけたか? と考えると結構厳しく感じる。それに対して、「Aを求めた後何を出力するか?」の部分は発想よりアルゴの力が求められた気がする。

 その部分で、(Bを全取り換えする場合の)最適解を求められていた(と思われる)という意味では、やるべきことはやっていたといえるけれど、もっと計算量を削減しなければヒューリスティックな手法は使えない。もっと考えなくてはいけなかったなぁ。

2024年9月17日火曜日

第11回 Asprova プログラミングコンテスト(AtCoder Heuristic Contest 037)

 141位。

コンテスト後のツイート

 マージした後のx+yの値が一番大きくなるような二点をマージする(マージの仕方はツイートと同じ)……という貪欲が強かった。
 これを思いつかなくてはいけなかったのだが、厳しかった。

 座標が大きい方から考えるのが良さそう、とは思ったけれど、コストをパラメーターに考えたくなってしまう。座標が大きいものをマージしたらコストも低くなる、とは考えにくかった。

AtCoder Beginner Contest 371

 Eまで。

コンテスト後のツイート

F - Takahashi in Narrow Road

 解説放送を見てAC。SortedSetを使った。

 解説放送を見て衝撃だったのは、$X_i-i$、$G_i-T_i$と座標変換して良いというところだった。これは思いつけない。
 これを思い付いていればそれ以降の部分は自力で解けた気がするので、ここが本質だと思う。しかし、言われてもかなり長いこと正当性が分からなかった。
 こういう変換ができることもあると覚えておきたい。

 

G - Lexicographically Smallest Permutation 

 大体自力でACだけど、中国剰余定理などという単語はツイッターで見た。

 順列を、どう巡回するかで分けるのはよくある方法。一つ決めるとその巡回部分は全て決まるので、それを使って最初の数字から決めていく。辻褄を合わせることができるか? とい部分で中国剰余定理を使えばOK。

 Fでなくこっちを解いていればコンテスト中に解けた気もする。
 ただ、コンテスト中は、Gの「辞書順」という単語を見て避けてしまったんだよね。もうちょっとちゃんと問題を把握してから解くか避けるか決めた方が良いね。









2024年9月14日土曜日

yukicoder contest 446

 Dまで。


No.2890 Chiffon

 一応自力AC。

 答え二分探索だが、最初の切れ目の位置を色々と試さないと無理そう。
 よく分からず、A[0]とA[1]の間で必要な分だけ試したらACできてしまった。ので、嘘かもしれない。(ただ、幅が長いとき、A[0]付近のものを試す必要はないので、そのあたりのものは捨てている。そのおかげで計算量が抑えられている可能性はある?)

 解説を読むと、長さが一番短いところを試せば計算量が抑えられるらしい。言われてみれば確かに。
 その方針は考えたのだが、計算量解析を思いつかず棄却してしまった。

No.2891 Mint

 結構解かれていたのに解き方が分からず、今話題のChatGPTに聞いたところ、商が同じものをまとめて計算すれば良い、と教えてもらった。なるほど! と思ったものの、コードは若干間違っていたため自分で書いたらかなり時間がかかった。



2024年9月13日金曜日

Codeforces Round 958 (Div. 2)

 Dまで。

コンテスト後のツイート

E. Range Minimum Sum

 解説AC。こたつがめさんの放送の振り返りも参考にした。
 また、Cartesian Treeはけんちょんさんのブログを参考にした。
 なかなか解説を理解できず、苦労した。

 まず、A[i]を取り除かない場合は、各iに対して、A[i]の左側で、A[i]より初めて小さい値が出てくるindexはどこか?(右側についても同じもの)を調べれば計算できる。

 その上で、差分計算で求めたい。
 このときの差分計算というのは、iを除いた場合とi+1を除いた場合で比較するというもの。全く取り除かない場合と比較するのではない。(結構後者を考えてしまったが、前者が自然ですね)

 そのとき、変更される部分がたいして多くない、というのが重要。取り除くindexがiからi+1へ変更したとき、Cartesian Treeにおいてiの左の子と、そのさらに右の子孫たち(及び、その逆側)、及び、i+1の左の子と、そのさらに右の子孫たち(及び、その逆側)しか変更されない。それらを変更すればOK。

 ただ、どこまで変更すれば良いかもO(1)でできるらしいのだがよく分からず、範囲最小値を求められるSparse tableを使って二分探索で求めた(Segment treeだとTLE)。