読書記録(2022)

kotatsugame.hatenablog.com

1月

  • 2日
    D級冒険者の俺、なぜか勇者パーティーに勧誘されたあげく、王女につきまとわれてる3
  • 3日
    VRゲーで最強無双の少年、現実にステータスが同期し人生逆転
    劣等眼の転生魔術師6
  • 4日
    クラスで2番目に可愛い女の子と友だちになった
    エプロンの似合うギャルなんてズルい
  • 5日
    神狩1〈上〉
  • 6日
    公務員、中田忍の悪徳
  • 7日
    機械音痴な幼馴染が我が家でリモート授業を受けているのは、ここだけの秘密。
  • 8日
    最強魔法師の隠遁計画14
  • 9日
    いずれ水帝と呼ばれる少年
  • 10日
    私立シードゥス学院III
  • 12日
    『ずっと友達でいてね』と言っていた女友達が友達じゃなくなるまで
    友人に500円貸したら借金のカタに妹をよこしてきたのだけれど、俺は一体どうすればいいんだろう
    転生魔王の大誤算4
  • 13日
    前世は剣帝。今生クズ王子3
  • 15日
    俺は星間国家の悪徳領主!2
  • 16日
    俺は星間国家の悪徳領主!3
  • 17日
    俺は星間国家の悪徳領主!4
    西野 ~学内カースト最下位にして異能世界最強の少年~9
  • 18日
    プリンセス・ギャンビット
  • 20日
    お隣の天使様にいつの間にか駄目人間にされていた件5.5
    VTuberなんだが配信切り忘れたら伝説になってた3
    青のアウトライン
  • 21日
    純白令嬢の諜報員
  • 22日
    デート・ア・ライブ アナザールート
  • 27日
    パシられ陰キャが実は最強だった件
    辺境都市の育成者5
  • 29日
    美少女エルフ(大嘘)が救う!弱小領地
  • 31日
    犯罪社会学者・椥辻霖雨の憂鬱2
    神狩1〈下〉
    カワイイけど慎重すぎるお嬢様の笑わせ方

2月

  • 2日
    宅録ぼっちのおれが、あの天才美少女のゴーストライターになるなんて。
  • 3日
    宅録ぼっちのおれが、あの天才美少女のゴーストライターになるなんて。2
    陰キャだった俺の青春リベンジ
  • 15日
    『ずっと友達でいてね』と言っていた女友達が友達じゃなくなるまで2
  • 17日
    護衛のメソッド2
    純白と黄金
  • 19日
    今日も生きててえらい!
  • 20日
    初恋のお姉さんを今度養うことになりまして
    最強魔術師の弟子は冒険者ギルドの始祖となる
  • 24日
    一生働きたくない俺が、クラスメイトの大人気アイドルに懐かれたら1

3月

  • 11日
    神々の権能を操りし者2
    創成魔法の再現者1
  • 12日
    迷子になっていた幼女を助けたら、お隣に住む美少女留学生が家に遊びに来るようになった件について
  • 15日
    超能力に目覚めたボッチが政府に呼び出されたらリア充になりました
  • 17日
    リモート授業になったらクラス1の美少女と同居することになった
  • 23日
    かくりよの宿飯 十二
  • 26日
    異世界でチート能力を手にした俺は、現実世界をも無双する10
  • 29日
    私より強い男と結婚したいの
  • 31日
    継母の連れ子が元カノだった8

4月

  • 2日
    公女殿下の家庭教師11
  • 18日
    男子だと思っていた幼馴染との新婚生活がうまくいきすぎる件について
  • 23日
    鴉と令嬢
    俺は星間国家の悪徳領主!5
  • 24日
    自炊男子と女子高生
  • 30日
    王様のプロポーズ2

5月

  • 3日
    転生ごときで逃げられるとでも、兄さん?3
  • 6日
    時々ボソッとロシア語でデレる隣のアーリャさん4
  • 10日
    最凶の魔王に鍛えられた勇者、異世界帰還者たちの学園で無双する2
  • 11日
    英雄と賢者の転生婚1
  • 24日
    お隣の天使様にいつの間にか駄目人間にされていた件6
  • 26日
    デート・ア・ライブ アンコール11
    僕たち、私たちは、『本気の勉強』がしたい。
  • 28日
    好きで好きで大好きなので、いっしょに好きを伝えたい

6月

  • 23日
    灰原くんの強くて青春ニューゲーム2
    才女のお世話3
  • 28日
    スキルが見えた二度目の人生が超余裕、初恋の人と楽しく過ごしています
  • 30日
    許嫁が出来たと思ったら、その許嫁が学校で有名な『悪役令嬢』だったんだけど、どうすればいい?

7月

  • 5日
    最強魔法師の隠遁計画15
  • 9日
    転生義経は静かに暮らしたい
  • 12日
    同い年の妹と、二人一人旅
  • 23日
    紅蓮戦記1
  • 26日
    一生働きたくない俺が、クラスメイトの大人気アイドルに懐かれたら2
    精霊少女に『カッコいい俺』だけ見せていたら、いつの間にか最強になっていた
  • 28日
    VTuberなんだが配信切り忘れたら伝説になってた4
    純白と黄金2

8月

  • 12日
    クラスで2番目に可愛い女の子と友だちになった2
  • 20日
    ありふれた職業で世界最強12
  • 24日
    ひきこもりの俺がかわいいギルドマスターに世話を焼かれまくったって別にいいだろう?2
    ログアウトしたのはVRMMOじゃなく本物の異世界でした2
  • 30日
    異世界でチート能力を手にした俺は、現実世界をも無双する11

9月

  • 12日
    D級冒険者の俺、なぜか勇者パーティーに勧誘されたあげく、王女につきまとわれてる4
  • 16日
    虚構推理
  • 17日
    虚構推理短編集 岩永琴子の出現
    虚構推理 スリーピング・マーダー
  • 20日
    虚構推理短編集 岩永琴子の純真
  • 23日
    虚構推理 逆襲と敗北の日
  • 26日
    お隣の天使様にいつの間にか駄目人間にされていた件7

週記(2022/09/19-2022/09/25)

09/19(月)

午後2時起床。先週の週記を少し書きつつ、もっぱらハーメルンを読み続けていた。午後10時前に1作読了。「犬とお姫様」。

syosetu.org

先週日曜日、つまり昨日読了した「女王様と犬」の続編で、比企谷八幡が高3、その他雪ノ下雪乃をはじめとする原作組がほぼ一律で高1となっている。雪ノ下陽乃は大1になっているので姉妹の年の差は原作と同じらしい。高1、高2と雪ノ下陽乃に付き従うことで鍛えられた比企谷八幡のスパダリっぷりが良い。年下になった原作ヒロイン組との関係やその中で改めて構成された原作と同じイベントたちが目新しく、非常に面白かった。

午後11時半を目前にして何とか週記の文章を作った。量が少ないのでハーメルンにかまけていても間に合ったのだ。読み返さずにいったん投稿。

CFの準備をしていたらスマホに通知が来た。30分後からTCO Finalsに関するオンラインミーティングがあるらしい。「午前12時から」と書いてあったので火曜日正午のことなんだなあと思っていたのに、実は日付が変わった瞬間だったようだ。心構えだけしてCF #821 div.2に出た。

Dashboard - Codeforces Round #821 (Div. 2) - Codeforces

Aはインデックスを\bmod kで分類してそれぞれ最大値を取る。Bはxyのどちらか一方が0で、もう一方がn-1の約数であればよい。Cはちょっと手間取ったが全要素を等しくすることができる。a_1と偶奇が一致するところだけ見て一番後ろの値を全体にコピーし、その後残りにa_1をコピーする。

D1はちょっと面倒。abで異なるインデックスを取り出し、奇数個であれば-1となる。偶数個なら必ず達成できて、特に4個以上なら2個ずつコストyで揃えられるので、これが明らかに最適。問題はちょうど2個のときで、ここを頑張って場合分けした。まず隣接していないならコストy。隣接しているとき、その左または右に2マス以上空きがあったら、そこを経由することで2yで揃えられるので、必要コストは\min(2y,x)

n\ge 5なので最後の条件は必ず満たされる。コンテスト中はこれに気づかずまだ場合分けを続けた。左右に1マスずつ空きがあった場合は\min(3y,x)になって、これも満たさなかった場合はどうしてもxかかってしまう。

D2はdp。あらかじめy\le xの場合をD1によって取り除くことで、先ほど場合分けしたような左右の空きマスを経由する場合を考える必要がなくなる。あとはどのようにペアにして揃えていくか。異なるインデックスだけ取り出したときに隣り合うペアをxで揃えるという遷移と、yによって揃えるペアの一方として確保する、または以前確保したものとペアにする遷移だけ考えればよい。

確保している個数を次元に持ってO(n^2)。実際は2個確保した段階でその二つをペアにしてしまってよいので、O(n)でも解ける。今x\lt yなので、うっかりインデックスとして隣接するペアをyで揃える遷移をしても問題ない。

ここまで解いたらちょうど日付が変わったので、オンラインミーティングに参加。その間はEの実験を行っていた。そうやって別のことに意識を取られていると、まず英語が全く聞き取れない。内容は全部チャット欄にまとめられた文章で把握していた。一方でEの考察も全然進まなかった。

ミーティングが終了してから改めて考えるとすぐ解けた。t秒後(x,y)にスライムがあるとしたら、それはもともとt-x-y秒の時点で追加されたものであり、それより前に追加されたt-x-y個のスライムだけが今注目しているスライムの動きに影響を与える。

ここで、(0,0)に追加されたt-x-y個のスライムのうち、\lceil(t-x-y)/2\rceil個が右に、残りが下に進んだとわかることに気づいた。以降同様に各マスにいくつのスライムが到達したかわかるので、これを全体に対して求めた後、t-x-y+1個目の動きをシミュレートすればよい。

全完7位。上に4人も日本人がいるのは何事だろうか。

ハーメルンを1作読了。「ようこそ元暗殺者のいる教室へ」。主人公の間延びしたセリフが好きになれなかったが、暗殺教室とよう実のクロスオーバーはいいなと思った。もっといろいろ読みたいと思って探したところ、ほとんど存在しないことが分かって残念。

syosetu.org

週記の校閲を済ませて午前4時くらいに布団に入った。30分ほどハーメルンを読んで就寝。

09/20(火)

午前11時起床。

布団に横たわったままハーメルンを1作読了。「RPG要素があるエロゲのRPG部分にドはまりしてエロそっちのけでハクスラするタイプの転生者」。日間ランキング1位だったので読んでみた。なぜ狂人と思われているのかの理由付けが上手い。最新話まで読み進められたくらい面白くはあったが、今の気分とは微妙にずれているように感じて、それほど印象には残っていない。

syosetu.org

昼食を摂り、しばらく本を読んでいた。午後5時前になってからノートPCの前に陣取り、インターン先の定例会に備える。月曜日が祝日だったので今日のこの時間にずれていた。

ノートPCにマイクがなくて、チャットによる参加になってしまった。先週の進捗はない。勉強会はOpenCVの話で、様々な画像処理や図形検出の関数が紹介された。幾何の問題として出くわしたら逃亡を選びたくなるほど面倒そうな処理がいくつも実現されていて、その手厚さに感動した。

夕食と入浴を済ませた後、リビングに転がって午後9時から2時間ほど寝てしまった。起きてからSRMに向けた準備をする。Appletを導入するつもりだったが、うっかり寝入ってしまってそれほど時間的な余裕がないので、今日もWeb Arenaで頑張ることにした。午前0時からSRM838。

https://community.topcoder.com/stat?c=round_overview&er=5&rd=19420

Easyはシンプルに実装が遅かった。最初にbitsetでdpして復元することを考えて、さすがに面倒だったので、選んだ要素を直接vectorで保持したままdpできないか考えてみた。定数倍もよくて十分間に合いそうだったので実装。サンプルを合わせてから、本当に間に合うかもう一回見積もりをやり直したり、最大ケースを試すかどうか迷って結局試さなかったりと時間をドブに捨て続け、提出したら200点を切っていた。

Medはバカ発見器で、自分は発見されてしまった側。ダイスの面数Sを昇順に見る。これまでの期待値をEとしたとき、新しい数が出る確率が1-E/Sになるらしいので、E\leftarrow(1-E/S)\times(E+1)+E/S\times E=E+1-E/Sと更新する。1/Sを前計算すれば十分高速。これに気付くのに30分以上かかってしまった。

後から計算して納得。期待値がE=\frac 1 A\sum_k kC_kと計算されたとする。ここでC_kは種類数kであるような場合の数で、Aはすべての場合の数の和、つまり\sum_k C_kである。ダイスを面数の昇順に考えているので、今の面数をSとしてこれまでの種類数は必ずS以下であることを用いると、各k=1\dots Sについて新しい数が出る場合の数は(S-k)C_k通り、そうでないものはkC_k通り。

よって更新後の期待値は\frac 1{SA}\sum_k((S-k)(k+1)C_k+k^2C_k)。整理すると\frac 1{SA}\sum_k(SkC_k+SC_k-kC_k)=\frac 1 A\sum_k kC_k+\frac 1 A\sum_k C_k-\frac 1{SA}\sum_k kC_k=E+1-E/Sとなり、先ほどの式に一致する。

Hardは辺の長さ昇順に見て、それ以前の辺によって端点が連結になっていない確率を求められればよい。これはABCで数え上げ版の出題がある。その場で再現できなかったので、少し時間を使って以下の問題を探し当てた。しかし数え上げを確率の計算に書き直せず、それっぽいものを書いて試そうとしているうちにコンテストが終了した。

G - Connectivity 2

チャレンジフェーズは何もせず。Hardが結構落ちてくれたので少し順位は上がったものの、27位となり、レートは2887→2785(-102)。今日はシンプルに僕が悪い。僕の頭が悪い。Hardはコンテスト中に書いていたものを完成させてみたがサンプルすら合わず。Nがやたら小さいことに注目し、UFの状態を全通り持つべきだったらしい。

読書に戻り、午前4時半に1冊読了。「虚構推理短編集 岩永琴子の純真」。第一話「雪女のジレンマ」が好み。雪女とその恋人の馴れ初めが延々語られ、推理は最後のアクセントに。その使い方も苛烈ではあったが人を幸せにする方向で、ハッピーエンドで良かった。ただこれだけで表紙にまで雪女が描かれるのはびっくり、と思っていたら第五話も雪女に関する話だったらしい。こちらも優しい終わり方だった。

さらにハーメルンを読んでいたが、午前6時を過ぎたあたりで寝落ちしたらしい。その後いったん目覚め、そのタイミングで就寝報告のツイートをしていた。

09/21(水)

午前10時半起床。午前11時からTA研修会というのに出たが、これは心得を読み上げるだけで10分程度で終わってしまった。

昼食を摂り、荷物を纏めて昼過ぎに実家を出た。仙台に帰る。母に車で駅まで送ってもらい、午後2時半の新幹線に乗った。車内では1時間ほどハーメルンを読み、残りは本を読んでいた。

午後7時帰宅。PCを起動してしばらく触っていたら眠気が強くなってきて、午後8時から午前1時半まで布団で寝ていた。

起きてノクターンノベルズを1作読了。「無愛想な転校生の裏垢を見つけてしまった。」。もうすぐ電子書籍になるようで、TLに流れてきた表紙イラストに興味を持って読み始めた。それなりに話数はあるものの思ったより進展が遅かった。主人公のリアルとSNSアカウントが紐づけられる展開を期待していたが、まだ描かれていない。

https://novel18.syosetu.com/n0085hq/

その後もノクターンノベルズを漁っていくつか読んでいた。朝が近づいてきて、さすがに明日のセミナーの準備をしないとまずい気持ちになるものの、なかなか手が動かず、結局午前8時になるまでずっとTLを見たりYouTubeを眺めたりしていたらしい。そこから1時間だけ前回の発表資料の残りに付け足したりして、午前9時就寝。

09/22(木)

午前11時半起床。二度寝は怖いしかと言って身を起こすほどの元気もなく、布団に横たわったまましばらくスマホを触っていた。正午くらいにやっと起き上がって準備し、出発。山の上の購買に閉店間際に滑り込んで確保したパンやおにぎりを食べ、午後1時からセミナー。

まず自分の発表が3時間弱。頻繁に止まって確認したり例を挙げたりしつつ丁寧にやっていたら、昨日新しく用意した部分まで進まなかった。なかなか伝わらず大変。うまい具体例を用意できなかったのはあるが、説明も悪かったのだろうか。証明を、「お気持ち」がわかりやすくなるようにと教科書のものから少し変えてみたら、必要な性質を正確に導くのがかなり面倒になったので、そこは失敗だったなと思う。感覚的には明らかなので深く考えていなかった。

次に博士の方のセミナーが2時間弱。先々週の続き。これまでずっと前提知識の部分で詰まっていたのが、じわじわ進んで今日ついに補題の証明に入り、感動した。肝心の証明は道具を念入りに確認していただけあってスムーズに理解できた。

午後6時帰宅。小一時間TLを眺めた後、思い立ってラノベの新刊チェックをした。一通り調べていざ注文、という段階で眠気に耐えられなくなり、布団へ。午後9時から午前0時半まで寝ていた。起きてからしばらくノクターンノベルズを読んでいた。

PCの前に戻ってリストアップしておいたラノベを注文。9月下旬から10月下旬まで一か月ちょっとの間に出る作品で、今回は28冊とかなり多めだった。新作にはそれほど手を出していないので、追っているシリーズの最新刊の出版時期が被っただけらしい。

午前3時から昼まで日記を書いていた。途中頻繁にYouTubeハーメルンで集中を切らしてしまうのでそれほど捗らなかった。書いている間に話数が少ないものを2作読了。

syosetu.org

「大学生、上杉風太郎。インフルエンサーでホストやってます」。五等分の花嫁は原作をほとんど知らないのに、これは完結後の話だったので、回想の描写から結末に至るまでどういう展開やシーンがあったか薄々想像がついてしまった。まあこれは原作を知らないのに二次創作を読むときの宿命か。内容については良くも悪くも感じていない。ただ、ホストっぽいキャラはよくても主人公がホストそのものというのは好きになれなかった。

syosetu.org

「国民的大物女優観察記録」。こちらの原作は全く何も知らなかったので、ほぼオリジナルと変わらない感覚で読んでいた。好感の持てる勘違い主人公で面白かった。エタりかけに見えるが、ちゃんと一段落するところまで投稿されていたのが嬉しい。

午後1時前に就寝。

09/23(金)

午後8時半起床。今日はコンテスト二つ。まず午後9時からCF #822 div.2。

Dashboard - Codeforces Round #822 (Div. 2) - Codeforces

Aはソートして連続する3要素を真ん中に寄せるのを全探索。Bは実験エスパーで各段の両端だけ1にした。Cは1\le k\le nに対してその倍数を全部見てどれを消せるかチェックすることで、逆に各値を消せるkの最小値が求まる。実際、小さい値から順にいま求めたkで消していけば達成できる。今考えてみれば、kを昇順に見て消せる値を貪欲に消しても同じことだった。

Dは難しかった。吸収したスライムの区間[L,R)として、aの累積和Sを取ることで必要な条件がS_R\ge S_Lと書ける。Lを固定してRを右に動かすことを考えたとき、先の条件を満たせる限界までいったん動かしてみて、その中でSが最大となるところを新しいRにして損しない。ただし端まで到達できるなら即座にYESとなる。Lについても同様のことを行い、どちらでも[L,R)を変化させられなくなったらNOである。

Eも難しい。a_{i,\ast}が交差iの等差数列になるように定めると、a_{r_k,c_2}-a_{r_k,c_1}\equiv r_k(c_2-c_1)となる。0\lt c_2-c_1\lt nかつr_1\ne r_2よりこの値はk=1,2で一致せず、よって条件を満たす。どうやって思いついたか定かではない。確か、行または列の値を揃えて少しずらすことを考えていたのだった。

Fも難しい。難しい問題だらけ。n+m\le 2^{k+1}を満たすkを取り、S_{[2^k,2^{k+1})}S_{[0,2^k)}のflipであることを利用することで、いくつかのより小さいn,mに分けて再帰的に求められるが、クエリの個数が爆発してしまいそう。しかしよく観察すると種類数が少なかったため、メモ化することで十分高速になった。トリックはこうだ:分割後にn+m=2^kを満たさないものは高々一つであり、さらにn+mが2べきの場合、以降何度分割してもその回数ごとに高々2種類のクエリしか生成しない。

Fに1時間かけて順位が崩壊した。全完19位。

シャワーを浴びて午後11時半からCodechef、September Lunchtime 2022 div.1。3時間半もあって大変。ペナルティがないので提出し放題だったのだが、各問題に60個程度テストケースがあって、提出するたびにいちいち神経をすり減らしながら待つ時間が発生して辛かった。

https://www.codechef.com/LTIME112A

INCADDはA_{i+1}-A_iを観察するとすぐ解けた。この値をすべて非負にしたい。区間更新はある範囲に+1したあと右端を大きくマイナスする操作になるので、常にR=Nとして操作することでそのマイナスを無視するのが明らかに得。さらにL=1とすることで全体に+1することにして、-\min_i(A_{i+1}-A_i)回操作すればすべて非負にできることが分かった。セグ木で管理してAの更新に対応。ただし操作回数も当然非負である必要があり、これはセグ木に乗せるモノイドの単位元0にするだけでは不十分だった。all_prodの際わざわざ単位元を掛けたりしていないのは当たり前。

ADJACNTPAIRSは簡単。最終状態を決めたとき、それと異なる値を一つ一つ揃えなければいけないのに加え、ちょうど一つずれたような連続部分列があればその長さlに対して\lfloor l/2\rfloorだけ余分に操作しなければならない。これは高々O(N)種類しかないのであらかじめ計算しておく。あとは最終状態の偶数番目と奇数番目としてそれぞれAにおける出現回数が多い順に値を試していく。O(N^2)通りあるが、明らかに答えを改善しない場合スキップすることでO(N)になる。なぜなら、偶数番目の値を決めるごとに、奇数番目の値としては余分に操作が必要ないもののうち出現回数最大のものしか見なくてよいから。

RMVNUMBERSは難しかった。相手の選択によらず適切に動いてAを空にできるか考える。これまで出現した値のLCMを今見ている値が割り切った場合、またはそのLCMが4\times 10^{14}を超えた場合にそうなるので、操作回数がある程度あればよさそう。2べきが昇順に並んでいる場合が最悪ケースのはずで、これでも50回も操作すれば十分。つまりM\gt 100のとき、先手も後手もやろうとすればAを空にできてしまうので、答えは必ず0となる。

あとはM\le 100の場合が解ければよい。Aを単純にvectorで持って再帰した。一段階再帰するたびにBで割り切れるか否かでAが完全に分かれるため、各深さにおいてvectorの長さの総和がNとなる。よって、空になったらさっさと0を返すのに注意すればO(NM)が達成される。コンテスト中は何を考えたかメモ化してしまい、0.49sec。この問題のTLは0.5secなので本当にギリギリ。日記を書いているときにこのメモ化が必要ないことに気づき、消して出しなおしたら爆速になった。

UNFRIENDLYは謎。どうせ持つべき候補が少ないタイプの問題だろうと思って、列の長さが\maxまたは\max-1なペアしか持たないようにしたら全然合わなかった。この下限をどんどん引き下げて何回も提出してみたが、TLには間に合っているもののWAのケースが全然減らない。そもそもA\le 3のケースですら盛大に間違えている。手元でランダムケースを作ったらすぐ落ちて、ようやく何がダメだったのか気づいた。

A\le 3のときの繰り返しパターンは2種類しかない。序盤でそのうち片方が一定以上優位に立ったとき、そこからもう片方のパターンに切り替えられないようだ。そこで、あるペア(a,b)を持つか決めるときに、対応する列の長さが\max-3以上という条件のほかに、すでにペア(b,a)を持っている場合も持つとするコードを書いてみた。とりあえずA\le 3のケースでは正しく動くはず。これで提出したら、なんと満点を取れてしまった。

残りは部分点。LCAINTERACTは直径を一つ求め、その端点の一方が根でないことを確認した後、そこから根がどれだけ離れているか二分探索で求め、その距離にある点の集合を半分だけ聞くのを繰り返して実際の根を特定するというコードでクエリ回数12回を達成した。直径の端点が根になってしまった場合に次数3の頂点を使った。中心を使うともうちょっと減らせそうだったがWA。BNDSPANTREEは区間スケジューリングで部分点1のみ。

合計481点で9位、レートは2799→2832(+33)、highest。BNDSPANTREEは明らかな必要条件からLRを調整するだけで区間スケジューリングの順序と辺の重みの順序が噛み合ってうまくいくらしい。すごい。パスに対するクエリに答える必要があってHLDを使いそう。自分もそろそろ履修しなければならない。

UNFRIENDLYの解法の正当性がよくわからない。下のツイートを元に考えてみる。持つか持たないか迷っているペアが発生するとき、\max\ge 4であるから、今のところ最適とされている選び方の直近四つが(w,x,y,z)と取れる。ここでw=zかもしれないが今は異なるとする。候補として確実に持っているのは(w,x),(x,y),(y,z)で、この三つのうちどれにも繋げることができないのは(y,x),(x,z)のみ。

実際、今確実に持っているペアを反転してみると、挙げた二つはそれぞれ(x,w),(z,y)に繋げることができる。しかしわざわざこのペアを選ぶ必要はないので、なぜこれだけチェックしておけば十分なのかがわからない。例えば(x,z)なら(w,x)というすでにあるペアにzを足すことで再現できるため問題ないと言えるものの、(y,x)のほうはどうやって吸収されているのか本当にわからない。

ちなみに、こういう考え方は\max-2以上を持つとしても同じところまで進める。なんと条件をこれに強めてもACしてしまった。\max-1ではさすがに落ちた。

食事して午前4時からTCB51に参加した。

https://techful-programming.com/user/event/3235

3問目はこの位置としては難しめに見える。ずらす幅を全探索し、対応する要素の差を足し合わせる。4問目は前から貪欲に操作しなければならない。5問目はfの和をA_i\sum_k B_k+MA_jと書き直す。\sum_k B_kの符号を見れば答えの候補は高々二つだが、念のためA_iを全探索した。

7問目はかなり面倒。ある頂点の親を1にするのが最適で、変更した頂点を根とする部分木に対する{\rm dist}(1,\ast)\maxはその場で計算、他の頂点のそれは全方位木dpのように計算して伝播してきた値を使った。

8問目は謎。最初にO(NM)のdpで値の種類数が1\dots\min(N,M)の数列を数え上げる。種類数がN未満の数列は必要な値をすべて含むような列ならどれからでも作れるが、そのような数列の数え上げにも今計算した値が使える。種類数がちょうどNの数列はそれ自身からしか作れない。

9問目は区間dp。10問目は|S|=cと決め打つとO(Nc)のdpが書ける。まだ決めていないSの要素数を持つことにして遷移をAの値で場合分けしてみると、A=-1の場合はdp_i\leftarrow dp_i+dp_{i+1}i=0\dots cで行い、A\ge 0の場合はdp_A\leftarrow dp_Adp_{c-A}\leftarrow dp_{c-A+1}だけ計算して残りは全部0にするという遷移になることが分かった。シンプルでいかにも速そう。これを実装すると、最悪ケースである全部A=-1のケースも手元で2.5secになり、提出してみると3.7secで通った。

理論値。時間は50分弱で、各問題ともボーダーまでかなり余裕がある。10問目はまたしてもTL 5secを悪用して定数倍高速化で通してしまった。改めて考えてみたところ、A=-1の場合の遷移を遅延させることでdp_{\ast}の非ゼロ要素が高々2個になり、具体的な値の取得も定数時間で行えて|S|を決め打つごとに計算量O(N)が達成できた。あまり見ない高速化で面白かった。

このあとYouTubeとネット小説に1時間ずつ時間を吸い取られてから日記を書き始めた。午前11時に切り上げてから少し読書。本を1冊読了。

「虚構推理 逆襲と敗北の日」。長編、ただし最初に短編のようなものが一つ付属していた。ちょうど解決シーンに入るところで数日読むのが止まっていたためちょっと没入感が薄い。そもそも解決自体割とあっさり目で、サブタイトルの意味もそこにはなく、続く主要キャラクターたちのやり取りがメインだった。この部分は興味深かった。岩永琴子はもうちょっと非人間的な性質だと思っていたのにそうではないようでびっくり。がっかりもしかけたが、その分は桜川九郎でバランスが取れているらしい。

ネット小説を読んで午後1時半就寝。

09/24(土)

午後8時半起床、午後9時からABC270。

TOYOTA MOTOR CORPORATION Programming Contest 2022(AtCoder Beginner Contest 270) - AtCoder

Aはbitwise ORを出力する。とりあえずRakuで書いたあと、もっと短くならないか4分ほど試行錯誤していた。

B問題は難しい。まずX\gt 0となるように適切に符合を反転しておく。0\lt Y\lt Xのとき壁を破壊する必要があって、Y\lt Zならハンマーを拾いにすら行けないため-1。そうでなければ|Z|+(X-Z)である。そもそも壁を破壊する必要がなければ答えはそのままXとなる。丁寧に確認していたらかなり時間がかかった。

CはXから祖先のリストを管理しつつdfsし、Yにたどり着いた時の状態を出力。Dはdp。Eは何周するか二分探索し、残りをシミュレート。Fは港と空港用の超頂点を用意し、その二つを連結成分に含めるか22通り全探索してそれぞれ最小全域木を求める。

GはX_i=A^i S+B\sum_{j=0}^{i-1}A^jと書ける。A=1は別に計算することにすれば、和を閉じた形にしてX_i=A^i S+B\times\frac{A^i-1}{A-1}=A^i\left(S+\frac{B}{A-1}\right)-\frac{B}{A-1}となる。これがGに等しいので、S\leftarrow S+\frac{B}{A-1}G\leftarrow G+\frac{B}{A-1}と置きなおすとA^i S\equiv Gが求めるiの条件。S=0は場合分けにより、それ以外はA^i\equiv G/SをBSGSで解くことで答えが求まる。

Exは難しい。しばらく唸っていたら綺麗な表現を見つけて一気に解けた。カウンターの操作を、初期値A_iであるような値C_iをデクリメントするかC_i\leftarrow A_iとするかで表し、状態をS=\max_i C_iで定める。この値が0になるまでの期待値を求めたくて、遷移はカウンターiを操作するときS\leftarrow\max(S-1,A_i)と書ける。これでループのある期待値dpに帰着できそう。

dp_Sを状態Sから0に遷移するまでの操作回数の期待値とする。E=\sum_i dp_{A_i}とおいて、dpの値をすべてEの一次式で表す。S=x\ge 1に対してdp_x=1+\frac{\sum_i dp_{\max(x-1,A_i)}}Nとなり、x-1\ge A_iなるiの個数とそれに対するdp_{A_i}の和が求まっていれば、適切にEから引いたりdp_{x-1}を足したりして計算できる。

最終的にdp_x=X+Y\times dp_{x-1}という形で書けて、XYは次にx=A_{\ast}となるまで変化しないので、その間の遷移をまとめて行うことができる。これによりS=A_1,A_2,\dots,A_Nのみ求めるのが十分高速。最後にE=\sum_i dp_{A_i}からEの値を求め、dp_{A_N}にそれを代入したものが答え。

本当にEを求められるのか、つまり\sum_i dp_{A_i}におけるEの係数が1にならないか不安だったが、エイヤと出したら通った。ではどんな値になるのかということでコンテスト後に式を追ってみたが、複雑すぎて断念した。

全完3位、日本人2位で賞金10万円。1時間ちょっとで全完したので時給10万円らしい。こんなことならAでコードゴルフしてなければよかった、と思ったが、実際のところ2位との差は10分以上あってどうしようもない。それより4位との差が2分ちょっとだったことのほうが重要で、B問題を丁寧に解いたことやG問題の式変形のコーナーケースを見落とさずノーペナで通せたことはかなり偉かった。

Gはこういう式変形をしなくても直接BSGSできるらしい。結局fのべき乗が同じ形で表せることが重要。解説のBonusは、通常のBSGSを非素数modで計算する場合と同じでf^{-1}を使わず頑張れるという話だと思う。このfでも\log Pステップくらい進めば周期に入るのだろう。

コードゴルフ。これはMHC後に縮めたものも含む。AはRakuよりAWKのほうが短くなった。Bは0XZの間にYがあるかで場合分けしていることになって、この「間にある」という条件は例えば(0<Y)==(Y<X)のように書ける。正確には区間のちょうど端にある場合に注意する必要があるが、今は制約から関係ない。これをAWKで実装したものが最短になっている。

Cは根をYとして各頂点の親を求める方法を実装したがコンテスト中の方法に負け。言語はPerl。DはAWKでdpしたらPerlより縮んだ。EはRuby。以降は手付かず。

午前2時からMHC R2に出た。

Meta Hacker Cup - 2022 - Round 2

A1は文字を取り除いたときに文字列の中心になる場所を2通り試して、左右に出現する文字ごとの個数の差が一つだけ1、残り全部0となることをチェックした。あらかじめ文字ごとに個数の累積和を計算しておく。

A2でも中心を2通り試すのは同じ。その前に列全体のXORを計算することで取り除く値が何であるかを確定させておき、チェックをmultisetのハッシュで行った。値に素数を割り当て、その積を\bmod pで管理する。このpとしては素数をたくさん用意してあり、提出コードでは7個だが、思ったより高速に動作したため手元で18個使っても出力が変化しないことを確かめておいた。ちなみに、原始根を考えることでこれが\bmod{(p-1)}における和を管理しているのと変わらないことがわかる。コンテスト後に気づいて拍子抜けした。

Bは各クライアントに対して上位K個のパスを管理する。遷移も、適切な順序でクライアントを見ることで日付ごとに上位K個のパスを更新しながら行えるため、十分高速。

Cが解けずDに進んだ。D1は左右の和の差が交換によって\pm 2\pm 4しかされないため、\pm 4、つまり13の交換を貪欲に行うべき。

D2は12しかないため交換しなければならない個数がわかって、それがc個だったとすると、Zから左に見てc個目までの1を右に見てc個目までの2と入れ替える、あるいはこれの12を逆にしたものを行うことになる。個数を管理するセグ木の上で二分探索することで範囲がわかり、その範囲内でインデックスの総和を求めると、差が答えになる。

Bが落ちて64点、178位。オーバーフローしていた。パスの利益は\max_i X_iで押さえられると思っていたのに、Y_i\lt X_i、つまり高く買って安く売る能無しクライアントのせいで壊れていた。

朝までC問題と格闘していた。はかりの左右どちらに乗せるかが重要だと思っていたが、対称性から無視できるらしい。とはいえ解説のこの部分はサラッと流されてしまっていたので、自分で説明を加えた。計算量は少し悪くなったものの間に合うオーダーに落ち着いたので満足。まずW_1と同じ重さのクッキーを1個以上はかりに乗せる確率を求める部分について、これは単に選ぶ順番(K+1)!通りを全部キャンセルしているだけなのでcombinationでよい。

次に、W_1と同じ重さのクッキーをk=1\dots C_1+Y個使う場合について考える。このケースに制限すると解説の文言が納得できた。k個をどのタイミングで選ぶか決めるごとに、そのうちどの位置に置かれたものが最後まで残るか一意に決定して、そこに置かれる確率はk個どれでも等しく1/kになるのだ。これによって、相変わらず選ぶ順番(K+1)!通りをキャンセルしてよいことがわかる。選び方を数え上げ、すべてのkに対して和を取った後、\binom{C_1+X+Y}{K+1}-\binom{X}{K+1}で割ることにしよう。

k個のうち0\le j\le k個がバッチ1由来である場合を考えると、まずこれが起こる場合の数が\binom{C_1}{j}\binom{Y}{k-j}\binom{X}{K+1-k}通り。そのうちj/kが当たりなので、これを掛けて足し合わせたい。Wolfram|Alphaに投げるとうまいこと閉じた式にしてくれて、C_1\binom{C_1+Y-1}{k-1}\binom{X}{K+1-k}/kになった。kは全探索できるので、これで解けている。

しばらくABCのコードゴルフの続きをした後、昼前から日記を書いていた。金曜日のCodechefの部分に差し掛かって、UNFRIENDLYを通した自分の解法の正当性が証明できず、布団に逃げ込んだ。午後3時就寝。

09/25(日)

午後8時半起床。午後9時から久しぶりのTOKI、TROC #27。

https://tlx.toki.id/contests/troc-27/

Aは列Aに偶数が含まれているか調べる。Bはそれより大きい最小の2^k-1までインクリメントし、一気に消すのが最適。

Cはインドネシア一流の謎パズルで、(N-2)(M-1)+M+1とそのNMを入れ替えたものの\maxを提出して1WA。4\times 4を考えて右上と左下を結ぶ線の上以外を暗くすることが可能だと気付いた。答えはNM-\max(N,M)

Dはやたら合わせるのに時間がかかった。F=1の場合は自明。F=2の場合、「先頭が1で隣接項の差が2以下の長さiの順列」をi\le Nに対して前計算しておけば、適当に足し合わせることで答えになる。この前計算は挿入dpを考えるのが正解だったらしい。隣り合う2項を交換するか決めるだけでは1,3,5,7,6,4,2のような並び方を生成できない。

EはM\le 2という制約に気づかず1WA。しばらく実験して、N以下で最大の2^k-1と、2^k\le Nならそれも聞くことで特定できることに気づいた。前者によって1\le s\le 2^k-1を、後者によって2^k\le s\le Nを区別できている。

Fは面白い。好きな辺を選んでそこまで往復することでウォークの美しさを\pm 2W_iすることが可能。これでユークリッドの互除法を行えるため、g=\gcd(W_1,W_2,\dots,W_{N-1})として2gで割ったあまりを求められる。この時点で、とりあえず根を一つ決めて各頂点までのパスの重み\bmod{2g}を求めておいた。それをd_uとすればf(x,y)\equiv d_x+d_y\pmod{2g}である。{\rm LCA}(x,y)より上のキャンセルも\bmod{2g}に吸収される。

d_x+d_y0(0,2g)[2g,4g)に分類するのは難しいな、と思っていたが、冷静になると\forall i.\;g\mid W_iなのだから、d_u0またはgだった。これなら0+02gに置き換えるだけでよいので簡単。

Gも面白かった。実験するとN回コピー後に値M\binom{N-1}{M-1}個あることがわかる。この値は\binom{N-2}{M-1}+\binom{N-2}{M-2}と等しく、先頭から\binom{N-2}{M-1}個は1回コピーをキャンセルしてもMで、残りはM-1になるということがわかる。このことから、NをデクリメントしながらDMを丁寧に更新するO(N)の方針が得られる。ただしcombinationは適切に差分更新を行う。

Nがとんでもなく大きいと困る。しかしM=3ですでに\binom{N-1}{M-1}=(N-1)(N-2)/2からN\approx\sqrt Dとなるため、M\ge 3では十分間に合いそう。よってM=1,2のケースさえ別に判定すればよく、この部分は実験で容易に解ける。

Hは解けず、7完8位でレートが2732→2762(+30)。highest。

10分のインターバルで午後11時半からCF #823 div.2。

Dashboard - Codeforces Round #823 (Div. 2) - Codeforces

Aはよい。Bからすでに難しかった。xでソートしx_k\le x_0\le x_{k+1}となる場合を考えるのを繰り返す。t_i+|x_i-x_0|i\le kかどうかによって絶対値記号を外せるので、それぞれ外す。x_0の項を除いて適切に\min\maxを求めるのは、あらかじめ左右から累積的に計算しておける。これを使うと最小化する値が\max(x_0-L,R-x_0)のような形になり、二つの値が等しい場合に最小値を取る。

Cは簡単。後ろから見て累積\minとならない位置の文字は動かすべきで、それもちょうど1回動かすとしてよい。するとどんな数字に変化するかわかり、残した累積\minの広義単調増加を壊さないような位置に挿入するのが最適になる。

Dは信じられないほど難しかった。ずっと実験で文字がどう入れ替わるか観察していたが全く光明が見えない。ふと不変量について全く考えていないことに気づき、その気持ちになって眺めると、s_1i文字目とs_2の後ろからi文字目がペアとなり、必ず異なる文字列に入ることに気づいた。ペアの列を並び替えたり、要素の順番を逆にするのは自由にできると信じる。

もし文字abのペアが二つあれば、文字列における対称の位置、例えば先頭と末尾に置くことでそのインデックスを揃えることができる。同じ文字のペアが奇数個しかない場合、それがaaのような形をしていれば文字列の中央に置ける。逆に、異なる文字のペアだったり、文字列の中央が埋まっていたりするともうダメ。この判定を書いたら通った。1時間かかったらしい。

次により多く解かれていたFを考え、bitDPをしても見るべき状態数が少ないから間に合いそうだと思ったが、とりあえず書いたらサンプルが合わず、そのままコンテスト終了。4完。かなりの大失敗だった。Bはx_0の位置にかまわず|x_i-x_0|=\max(x_i-x_0,x_0-x_i)として展開してしまう方法が賢い。コンテスト中のようにLRを使った形に整理できる部分は同じながら、x_0の範囲が制限されておらずここから即座に答えが出る。

コンテスト後にFをupsolveした。まずbitDPの説明から。インデックスiを見ているとき、v=p_{q_i}としてここに置ける値の範囲を考えてみる。まず自分より左にi-1個の値があって、これらはすべてv+k以下である必要がある。そのような値はv自身を除いてv+k-1個しか存在しないため、v+k-1\ge i-1よりv\ge i-kが得られる。同様のことが右に対しても考えられて、v\le i+kもわかる。

つまり値をこれまで置いたかどうか記録しておかなければならない情報が2k+1個分、v=i+kはインデックスiで初めて置けるようになった値なので、正確には2k個記録しておけば十分で、bitで表現できる。

端を除けば2k個のうちちょうどk個が埋まった状態しか考えなくてよい。k=8で計算してみると\binom{16}{8}=12870通りだった。これで出してもTLEしてしまうが、ここで「また値vを置いていないのにv+kより大きな値が置かれている」という状態をちゃんと無視することで十分高速になった。値vを置くときにダメだと気付くのでは遅く、状態数が多くなりすぎるようだ。そのほか、転倒数をあらかじめi-k\le v\le i+kに対して計算しておくなどする高速化を入れて提出すると、1400msで通った。

TCB51の結果が出ていた。理論値は3人いたが時間差で優勝。10問目を非想定で押し通しているとはいえ、2位と20分以上差をつけていて気分がいい。

来週水曜日にたいぺーとホスフィンと3人で家で飲む予定。いつもはドンキでお酒を購入するのだが、今回はAmazonで見繕ってあらかじめいくつか購入しておくことになっていた。ギフト券残高の消化。これの希望が出そろっていたので、しばらく時間を使ってそれが本当に最安値か調べたりした後注文した。

午前4時から午前11時まで月曜日のインターン先勉強会のための発表スライドを作っていた。内容は考えてあったので今日一日で何とか完成。

途中でRating Historyを確認したらAtCoderCodeforcesのAC数が両方ほぼキリ番だった。

生協に行って菓子パンやラノベを購入し、正午就寝。

週記(2022/09/12-2022/09/18)

09/12(月)

午後1時起床。布団に横たわったまま1時間くらいTLを眺めていた。シャワーを浴びて生協とコンビニへ。予約していたラノベを受け取り、パンやお菓子を買ってきた。帰ってきてしばらく先週の週記を書き進めていた。

午後4時半からインターン先定例会。先週火曜日の1on1までを進捗として報告。週あたりの稼働時間はこれまでと特に変わらないが、今回は求めるものにかなり近い出力が得られているので、ウキウキで発表した。今週は帰省のため全く稼働しない予定。

勉強会はビームサーチの話だった。焼きなましはそれっぽく書いてもまあまあ動くのに対し、ビームサーチはうまく行った試しがない。そもそも自分のマラソンマッチ経験が少ないからというのもあるだろう。今日は基本的なこと、どのような実装上の工夫があるかなどに焦点を絞って聞いていた。

終えてからもずっと日記を書いていた。午後11時半になって一通り文章が出来上がったので投稿、即座にCF #820 div.3に出た。

Dashboard - Codeforces Round #820 (Div. 3) - Codeforces

Aはa-1|b-c|+c-1を比較。b=1のときの注意書きを誤読し、最初は場合分けしていた。サンプルで落ちたので改めてしっかり読むと真逆で、特別扱いしなくてよいと書かれていたのでびっくり。Bは後ろから復元すると簡単。Cは先頭から末尾に一気に飛ぶのがコスト最小の経路の一つ。文字コードに関してその間にあるマスを全部踏んでもコストは変わらないから、それを出力する。

Dはy_i-x_iを見て、最大と最小をペアにできるならするのが最適。ペアにできない場合、最小のほうをグループに含めようとするとそこにy_i-x_i\gt 0なる人を二人以上使う必要があるが、それをするくらいならその二人だけでグループを作っても変わらない。つまり最小のものを無視してよい。dequeで実装した。

Eは微妙に二分探索できない制約なので乱択しろと言っている。3頂点の間で計3回聞いてループを作ることにした。パス2本のうちどちらが答えとして返ってくるかは2^3通りあって、そのうち5通りではそれぞれ答えが一意に確定するようだ。つまりそれらを候補とし、今度こそ二分探索することで正しい答えが求まりそう。

二分探索にかかるクエリ回数を適当に見積もった結果、9頂点の完全グラフを聞いてそこから\binom 9 3個のループを取り出し候補を作ることにした。辺がたくさん重複しているので、1ループあたり候補に答えが含まれる確率が先ほど求めた5/8になっているかは不明。信じて投げたらWAが出てしまった。

今度はループに含まれる辺に重複がないようにしてみた。ループの数は13個になってしまうが、これでも失敗する確率がケース当たり(3/8)^{13}で、50ケースあるのだから全体の成功確率は(1-(3/8)^{13})^{50}\approx 0.9999。この計算でかなりの自信を持ちつつ提出。しかしこれもWAだった。

流石に何かおかしいと思ってコードをチェックすると、クエリを投げる関数の引数がint型になっていた。ここをlong longに修正したら上の二つの方法はどちらも通った。つまらないミスで2ペナと+10分、残念。

Fは\bmod 9が聞かれているので桁和に言い換え。あらかじめv(l,r)\bmod 9kのすべての組み合わせを試しておき、クエリに答えた。しかしこの前計算も定数時間で行えているので、素直にその場で求めてもよかったかもしれない。

Gはdp。sを先頭から見て、tの出現があったらそれと被るような区間をドットに置き換える。どこを置き換えるかは全探索し、置き換えた先の文字に飛ぶ。最初にsのどこにtが出現するか列挙しておいたら計算量がO(|s||t|)になった。制約は3乗を許すくらい小さいので謎。

全完23位。Eは2頂点を(a,b)(b,a)の形で聞くのでも良かったらしい。1\le a\lt b\le 6の範囲で15セット聞くだけでも、それぞれ1/2の確率で失敗するので全体の成功確率は(1-(1/2)^{15})^{50}\approx 0.9985で、通った。クエリ回数は33回程度なので、この確率はまだいくらか上げることができる。

先週の週記の校閲に入り、午前4時完了。しばらくコードゴルフしてから布団に入り、ラノベを1冊読んだ。「D級冒険者の俺、なぜか勇者パーティーに勧誘されたあげく、王女につきまとわれてる」4巻。

この巻はシリアス+バトル。3巻までの内容をあまり覚えておらず、伏線に言及されてもピンとこなかったり、前の巻で取り扱われたキャラを新キャラだと思い込んでしまったりしたが、メインの1巻からずっと出ているヒロインに関してはいくらか覚えていたのでついていけた。終盤は主人公の無双シーン。日記での言及を読み返す限り、3巻はそういう描写がなくて消化不良気味だったらしい。今回は読めて嬉しい。相変わらずの理不尽な強さは、これからだんだん背景が明らかになっていきそうな予感。

ハーメルンを開いて最近のランキングをチェックし、1作読んだ。「依存症な彼女たち」。まだ2話しか連載されていないが、今のところ非常に面白い。なんとなく淫靡な雰囲気が気に入った。主人公の動じなさも好み。

syosetu.org

午前8時半就寝。

09/13(火)

午後4時起床。1時間くらいスマホを触ってから身を起こした。準備を整えて外出。ゲーセンに向かう。

油そばで腹ごしらえをして、午後6時半から5時間くらい遊んだ。今日はひたすら新曲。それなりの難易度の譜面がいくつも追加されて、それぞれ粘着してAJを捻り出そうとしていた。

端に接するように配置されたフリックは、その端の1マスともう一箇所を含むように触れるだけでJC判定が出る。これを利用して手をあまり動かさずにフリックを拾う、いわゆる端フリックと呼ばれるテクニックがあるが、これを嫌ったのかこの譜面ではひたすら内側にフリックがあって大変やりにくかった。

普通の譜面と同じ感覚でやると外側に弾くように手を動かしたくなるのに、それだとアタックが出てしまうので、意識して内側に向ける羽目になり、プレイしていて気持ちよくない。そもそも端フリック判定というのは、多少のアバウトさを許し爽快にプレイさせるためのものであるからして、これを回避されては苦しい。

中盤も終盤も難しい。ホールドをある程度無視することで中盤の勝率が上がったと思ったら、無事終盤で癖が付いて大変だった。ちょっと運指を組んで解決。

プレイ真っ最中の午後7時過ぎ、TLでPAST11に関する言及を見たので、さてはと思ってAtCoderを確認したら過去問が公開されていた。出先にいてコードゴルフできない。慌てて最初数問を見たが、そんな一瞬で最適っぽいコードが出そうなものはなかったので、とりあえず安心。

帰宅してから数時間ほどコードゴルフしていた。

第11回 アルゴリズム実技検定 過去問 - AtCoder

とりあえずHまでとJを解いた。DFHは特に縮みそうな気配がない。

AはX/A+C-X/Bの符号を見る。符号を見るときはdcでRコマンドを使うのが定番で、このとき適当に掛け算して非ゼロの場合の絶対値が3以上になっている必要がある。ところがこの式にBを掛けたものがすでにその条件を満たすらしい。すると式自体もかなり書きやすくなって、いい感じに縮んでくれた。

BはRakuのマッチングでExhaustiveオプションを使うのが強い。取り出す区間に重なりがあっても構わず、あり得るすべてのマッチを取り出してくれる。実行時間だけが心配だったが、かなり余裕を持って間に合ったのも嬉しい。

Cは整数と浮動小数点数にあまり区別のない言語だと単にべき乗を計算するだけで十分である。なぜなら109くらいなら精度を落とさず表現できるから。試した中ではPerlで書くのが短い。

Eはi=\lfloor\sqrt{N-1}\rfloorとして|N-i^2-i-1|+1が答えとなる。これはすでにdcで隙のないコードが提出されていた。Gは過去の問題を漁って、グラフを連結にするためにはあと何辺必要かという問題を探し、それの最短コードを少し弄って提出した。ただの判定ならもうちょっとなんとかなりそうではある。

C - Connect Cities

JはRaku。普通に日付のRangeでループしようとしたら、最大で300万回弱回すことになって流石に間に合わなかった。日付から、暦の起算日から数えて週末が何回あったか求め、差を取る方針に。daycountメソッドで得られる日数のカウントが1858年11月17日水曜日を0として数えたものであることに注意しつつ、頑張って式を作った。

明日帰省する予定。その前にやっておくこと、クラウドに上げておいたほうが良さそうなデータなどを確認していた。途中TLを見たらProject Eulerを埋めていた人がいて、つられて自分も1問挑戦してみたが、結局解けなかった。

午前6時に布団に入った。1時間ほどハーメルンを読んで寝落ち。

09/14(水)

午後1時起床。昨日に続いてハーメルンを少し読んでいたが、帰省のためにはそう悠長にしている時間はなかった。布団から脱出して準備開始。

仙台にいる間にやっておきたいこととして、月曜日読んだラノベの感想を日記に書くことと、前期の単位の取得状況をツイートすることがあった。それぞれパパっとこなした。以下のツイートの画像には含まれていないが、5月下旬頃に一週間グラフ理論の集中講義を受けていて、これは2単位分。無事AAだった。

いよいよ集中講義一日目である。

週記(2022/05/23-2022/05/29) - kotatsugameの日記

今回の帰省では「虚構推理」シリーズの既刊5冊を一気に持ち帰ることにした。以下に引用したような経緯があって、今がちょうど良い機会だと思う。

漫画を読んでいた。虚構推理の1巻と、転スラの10巻から15巻あたり。前者は小説版を積んでおり、序盤を漫画で確認することで読む意欲を高めようとした。

週記(2022/09/05-2022/09/11) - kotatsugameの日記

午後3時を回ってから出発。仙台駅に向かい、まずみどりの窓口で切符を購入した。9月のド平日なのに大量に人が並んでいて、30分くらいかかった。券売機の方も大混雑で一体何があったのか謎である。

それから一旦ゲーセンに向かって2クレだけプレイした。明日のアップデートで終わってしまうイベント、チュウニズムデュエルがあって、まだ走りきれていなかったのだ。昨日しこたま進めただけあって無事完了。

今日は昨年11月の超大型アップデート以来始めての旧筐体でのプレイで、その60fpsと新筐体の120fpsが思ったより激しく違うことを体感できた。早い話、感覚が全然合わずひたすら下手くそだったのだ。

仙台駅に戻って新幹線の時刻を調べたが、ちょうど良い接続のものはつい10分ほど前に出てしまったらしい。そもそも大宮から黒部までは1時間おきにしかなく、今からだと指定席に課金しても見当をつけていたものには間に合わないようだ。到着時刻がその分遅くなってしまうが、まあ仕方ない。

1時間遅い列車に乗るとすると、仙台から大宮までは自由度が2だけあって、仙台でたっぷり待つか仙台と大宮で半分ずつ待つかということになりそう。もう一度ゲーセンに行く元気もないのでとっとと移動を開始することにし、後者を選択した。

新幹線の中ではずっと、昨日寝る前に読み始めたハーメルンを読んでいた。到着ギリギリまでかかって1作読了。「女王の女王」。

syosetu.org

面白かった。男女ペアの主人公はかなり好みの要素である。片方が原作キャラというのは、そもそも原作を読んでいなければ全く気にならない。主人公が常に混ぜっ返すような態度なのは少し気に入らないが、それ以外は堂々たる最強格として君臨しており、良い。原作の展開と合わせるためか序盤はあまり主人公ペアが主導権を握らなかったので、そこで焦らされた分その後の展開がまさに満を持してという感じで爽快だった。

駅から実家までは父の迎えの車。午後10時を過ぎてから帰り着いた。夕食・入浴を済ませ、布団に転がって本を読んでいたら日付が変わるくらいに寝落ちした。

09/15(木)

午前4時前に目を覚まし、また変わらず本を読んだり、ハーメルンを読んだりしていた。両親の起床に合わせて朝食を摂り、布団に戻ってまた午前8時頃に寝た。

午後3時半起床。寝ている間にDMが届いていた。見ると明日発売予定の「競技プログラミングの鉄則」という本に付属する演習問題のジャッジURLだった。正式な発売前に手に入れた方のブログ記事に載っていたらしい。コードゴルフの時間だ!

競技プログラミングの鉄則 演習問題集 - AtCoder

とりあえず最初の数問を見ると、すでに自明な最短コードが提出されていた。これは同じようにやっていては一つも取れないぞと思って問題をつらつら眺めていたら、B01も同じくらい自明なことに気づいた。よって問題番号がBから始まるものから埋めていくことにした。

ちょっと複雑な問題、簡単に縮みそうにない問題は容赦なく飛ばしていって、3時間くらいでBとCを一通り確認した。Aのほうは流石にもう目ぼしいものは残っていないだろうと思えたので、とりあえず一息ついて夕食を摂った。

夕食後、両親に「笑わない数学」というテレビ番組をおすすめされたので、録画が残っていた暗号理論の回とABC予想の回を視聴した。

暗号理論の方は手を動かしやすい話題ということもあって面白かった。暗号の歴史や素因数分解の困難性というアイディアを紹介したあと、実際にRSA暗号を計算するところまでやっていてびっくり。

ABC予想の回は微妙。そもそも{\rm rad}の定義を一切説明してくれなかったので以降の話も全部あやふやだった。この回でも頑張って手を動かせる話題を探したらしく、a=2^nb=3^nとしたときa+b素因数分解するとp^1または5^2しか現れないことを計算で確かめていて、この事実はなかなか強いことを言っているなあと思ったが、式変形が追えないのは残念だった。

後から{\rm rad}の定義を調べて納得。これは正整数に対し、それの互いに異なる素因数の総積を表す。このもとで、先ほどのa,bを用いてABC予想ステートメントa+b\lt K(\varepsilon){\rm rad}(ab(a+b))^{1+\varepsilon}から上の主張を導こう。ただしこのa,b\varepsilon\rightarrow 0のときK(\varepsilon)\rightarrow 1としたのは番組でも画面下に注釈があった。

a+b23を素因数に持たないため、abとは互いに素。よって{\rm rad}(ab(a+b))={\rm rad}(ab){\rm rad}(a+b)である。{\rm rad}(a+b)を移項することで(a+b)/{\rm rad}(a+b)\lt{\rm rad}(ab)={\rm rad}\left(2^n\cdot 3^n\right)=2\cdot 3=6。つまりa+b=\prod_i p_i^{e_i}素因数分解したとき\prod_i p_i^{e_i-1}\lt 6であり、p_i\ne 2,3なので、e_i-1\gt 0なるiが存在すればそれは(p_i,e_i)=(5,2)に限られるということがわかる。

部屋に戻ってまたコードゴルフ。すぐに眠気が来て、布団に転がって2時間ほど寝たあと、今度は午前7時までずっとコードを縮めていた。

途中C++のコードを書こうとしてファイルを開いたら、ABC266-Exを3D BITで解こうとしたときのコードが残っていたので、完成させて提出しておいた。座圧用のインデックスをBITにおける上位のノードに伝播させておらずバグっており、それを直したら無事4sec弱で通った。3Dセグ木は確か手元のランダムケースでも16secかかっていたはず。定数倍の差はそれほどまでにあったらしい。

atcoder.jp

朝食を摂り、少し日記を書いた後外出。地元で済ませる必要のあった用事を二つばかり片付けた。本来は木曜午後に行う予定だったが、思いがけず大量の問題が公開されたので今日に延期してもらったのだった。

正午前に帰宅。昼食を摂ってしばらく本を読み、午後2時前に就寝。

09/16(金)

午後10時を過ぎてから起床。夕食を摂って、TAの書類作成に取り掛かった。今日が期限である。印刷→押印→スキャンの作業に思ったより手間取ったが、なんとか日付が変わるギリギリに完了することができた。仙台にいる間にできれば良かったのだが、そもそもこの書類の提出を求められたのが火曜日で、気づいたときにはもう帰省秒読みに入っていたのだ。

少しコードゴルフしたりハーメルンを読んだりしてから読書に入った。午前4時頃になって本を1冊読了。「虚構推理」。

面白かった。ちょくちょく叙述トリックみたいなものが挟まれてその度にびっくりする。少なくとも序盤のそれの種明かしについては、漫画版だと大ゴマで強調されていたのでわかりやすかったが、小説版だと特に前後に空行があるわけでもなかったので、そういう演出を必要としないのは潔いなと思った。

解決編は100ページばかりを一気読みした。もともとどういうスタイルのミステリかはタイトルやあらすじにあるし、それがこの作品のポイントになっているのだが、実際に虚構の推理を実現されると、真実を知る自分でもそうかと思えるような納得の行く論理展開で、まさに狐につままれたようだった。期待以上。

次の巻である短編集を読み進めつつ、朝まで日記を書いていた。朝食を摂り、また読書に戻る。午前11時半就寝。

09/17(土)

午後2時45分の目覚ましで起床。午後3時からのAHC014に合わせたものである。開始と同時に問題文を読み、提出してまた就寝。

estie Programming Contest 2022(AtCoder Heuristic Contest 014) - AtCoder

今日は外食に行くと聞いていたので午後4時過ぎに起床する予定だったが、実際に起こされてみると午後8時でびっくりした。どうやら僕の睡眠時間が短く細切れだったのでなしになり、僕も目覚ましを掛けなかったのでこうなったらしい。かなり申し訳ない。

最近の自分の感覚ではこのくらいの睡眠時間でも頑張って起きて活動するのは珍しくなかったので、遠慮なく起こしてもらって構わなかったのだが、高校生の頃は非常に寝起きが悪かったのでその頃と同じ対応をされたんだなあと思った。

夕食を摂り、午後9時からABC269に出た。

UNICORN Programming Contest 2022(AtCoder Beginner Contest 269) - AtCoder

Aはdcで書いたら入力に負号が含まれていてWA。AWKで書き直した。そこからは特に工夫なくC++。Bはよい。CはいつものO(3^N)bitDPで使うループ。

Dは問題ページを開いた瞬間六角座標が見えてげんなりしたが、隣接マスへの差分がiまたはjの偶奇によらず決まる形だったのでかなり書きやすかった。AOJ-ICPCなどでは、そうでない面倒な座標の取り方をされた問題ばかりだったように記憶している。

Eは縦横それぞれ二分探索。[1,(l+r)/2]を聞いたのに[l,(l+r)/2]で判定してしまい1WA。Fは二次元累積和と同様マス(1,1)を左上とするようなクエリ四つに分解し、それぞれ気合いで計算する。最初は書かれている値の偶奇で0に書き換えられるか決まると思っていたので、M\bmod 2を見る場合分けをしてしまった。

Gは最初全部表向きにした状態からB_i-A_iを何個使うかという問題だと捉えた。この値はO(\sqrt M)種類しかないので、それぞれO(M)でdp遷移ができれば嬉しい。実際、|B_i-A_i|本のdequeを用意してそれぞれスライド最小値を行うことで、不可能な遷移を削除しつつ計算できることに気づいた。ちょっと割ったり余りを取ったりの操作が重いかなと思いつつ提出したら2973msで冷や汗を垂らした。

Exは2乗の木DPを畳み込みで高速化したら当然のようにTLE。結局解けず、7完25位。

コードゴルフ。AはAWK。最初に提出したのが31Bで、その後Bashからdcを呼び出す方法でも31Bを達成し満足していたが、30分くらい後になって改行を空白にしてもACできることを思い出した。これによりAWKのコードが1B縮んで最短となっている。Bは適当にRaku。[Z]で転置を再現するのは何回か使ったテク。

Cは普通にループするとNから減らしていくことになるので、その値をNから引いて出力することで昇順にした。また無限ループしないように条件を定めようとすると出力が1行足りなくなるのは、判定と同時に出力を行うことで解決。EはRubybsearch。他は手つかずである。

日付が変わったあたりからはずっと本を読んでいた。2冊読了。

「虚構推理短編集 岩永琴子の出現」。虚構推理シリーズの2巻として位置づけられているが、講談社タイガにおける発売日は最も早いのでちょっと分かりづらい。収録されている短編のうち第二話「うなぎ屋の幸運日」は他とは毛色が違い、岩永琴子以外の推理がメインとなっており印象的だった。

「虚構推理 スリーピング・マーダー」。シリーズ3巻となる長編、と言っても短編三つと中編が緩やかに連携する感じだった。この中編は主人公ペアの異質さ悪辣さが前面に押し出されていて非常に楽しく読めた。と言ってもただ職務に忠実なだけだったと説明はあるが。虚構の推理を単純にラストに持ってくるのではなく、そこからもう一捻りあったのには驚かされた。

朝食を摂り、布団に入ってしばらくハーメルンを眺め、午前9時就寝。

09/18(日)

午後4時半に起こされた。今日こそ外食、焼き鳥屋に行く。

開店から数分後には店に着いたものの、その時点で待ちが数組いて、当然今いる客も食事を始めたばかりなのでテーブルに案内されるまで1時間半ほどの待ち時間があった。その間にハーメルンを1作読了。「真の実力はギリギリまで隠しているべきだったかもしれない」。

syosetu.org

全体的にギャグ風味。こういうのはサッと完結するのが一番好みで、これも11話で一応の完結を見た。そこまでは面白かった。しかしその後も連載は続いている。蛇足とは思わないが、相変わらずギャグ展開を多めに混ぜ込むので風呂敷が広がり続け、だんだんよくわからなくなってきた。さらにエタっているのでちょっと印象は悪くなった。

1時間弱食事して帰宅。

帰ってきて、今日はコンテストが何もないので寝っ転がってハーメルンを読んでいた。また1作読了。「女王様と犬」。

syosetu.org

雪ノ下陽乃の高校時代をメインに据えるにあたって関係者の学年が色々ずらされており、特に城廻めぐりと同級生になるというのはかなり目新しかった。内容も非常に面白い。雪ノ下陽乃の印象は原作のような悪いものではなく、ただ万能なサディストという描かれ方をしていて、ヒロインとして違和感がない。まさかくっつくとは、というのは原作を読んでいるときも違う相手に対して思ったことか。エピローグとして未来の風景が描かれるのも大好物だった。

入浴後はハーメルンをチラチラ読みつつずっと日記を書いていた。完成しないまま午前5時過ぎ就寝。

今週は帰省にお誂え向きでコンテストが全然なかった週だった。特に日曜日が丸々空くというのは何時ぶりだろうか。その時間で参加記を完成させるでもなく、相変わらずハーメルンに費やしてしまったのは残念だが止めようがない。そうやってずっと実家でグダグダしていたこともあり、この週記は12000文字弱と普段に比べ量が控えめになった。

週記(2022/09/05-2022/09/11)

09/05(月)

午後4時過ぎ起床。布団から何とか這い出して、半からインターン先の定例会へ。

進捗報告は先週とほぼ同じで、試したことがダメだったのでまた新しい方針を実装しますというだけ。勉強会はPythonについて、3.6以降のバージョンアップに伴う新機能を紹介するものだった。このあたりでコードゴルフ的に一番インパクトがあったのは代入式、いわゆるセイウチ演算子だろう。それに関しても面白い話が聞けた。

Pythonに変数のスコープなどあってないようなものだが、リスト内包表記だけは特別で、ちゃんとスコープを作るらしい。このことはこれまでは、ループ用の変数が外の変数と被っていても外のものを書き換えないということを意味していた。ところが今、リスト内包表記で実行するループ内で新しく変数に代入する場合、それはループを抜けても保たれる。つまりスコープはもはや正しく動作していない、という話があるそうだ。

これに関係するのかどうか知らないが、リスト内包表記のリスト部分で代入式を使おうとすると文法エラーとなる。よく引っかかってしまい、おかしな制限だと思っていた。裏にこういう背景があったのかもしれないと思えば納得もできそう。

終えてからはずっと先週の日記を書いていた。ARCの残りと5hについてを書き上げればとりあえずは完成なので、これまでに比べると完成までは非常に近かった。ちゃんとそこから校閲まで済ませて、午後11時過ぎに投稿。

しばらくコードゴルフをした後、午前1時くらいに布団に入ってハーメルンを読み始めた。午前3時過ぎ寝落ち。

09/06(火)

午前8時過ぎに目を覚ました。昨夜読み始めたハーメルンを読了。「知識も無いのにポケモン世界にチート転生したが何も面白くない」。

syosetu.org

主人公にはポケモンバトルの才能という転生チートがある。しかしそれの実現方法があまり好きではなかった。ネタバレになってしまうが、主人公がチートに操られる感じだったのだ。能力に振り回されるのともまた違うとは言え、やはり基本は主人公が能力の手綱を握っていてほしかった。そういう能力を持っているが故の性格で主人公がポケモンのことをあまり顧みていないのも辛い。

眠気を待ちながら別のハーメルンを読んでいたが一向に眠れなかったので起き上がった。ゴミを出してからしばらくコードゴルフして、午後1時過ぎに学食へ。昼食を摂り、予約していたラノベを受け取って帰ってきた。

DMOJのコンテストが終わっていた。先週土曜日深夜の3時間で参加したもの。ここに感想を書いておこう。

An Animal Contest 7 - DMOJ: Modern Online Judge

P1はConnect 4を知らず問題文が読めなかった。w\times hの盤面にトークンを「落とす」というのだから実質3次元の盤面なのかと思ってしまった。張られていたWikipediaへのリンクから実際の画像を見て理解。それからもコーナーケースにはまり込んで2WAした。h=1なら先手四つ後手三つのトークンのためw\ge 7が必要。w=1なら交互に積み重なるだけなのでダメ。残りは自明な条件のh\ge 4\lor w\ge 4をチェックすればよい。

P2は出力する木の根を指定していないのにジャッジで先祖・子孫の関係を使うらしく、パッと見意味不明でclarでも投げようかと思った。しかしよく考えると、元の木における関係を見ることで出力の条件からどの頂点が根であるべきかは高々一意に定まる。そこまでジャッジで面倒を見てくれるのだろうと判断して解いた。

解法は深さの偶奇で木を分けるというもの。これでK=2が達成できて最適、と思って書き始めたが、根と隣接している頂点を一つの木にまとめられず、もう少し考えることになった。考えた結果、根と隣接している頂点はすべて出力した木のどれかの根である必要があるということが分かったので、K=2とは限らないもののK最小は達成できている。出したら通った。

P3はiを固定してi\lt j\le Nに対する和を一度に求める。一番近いポータルからの距離はまとめて線形時間で計算できるので、最初に求めておく。これを使うとある地点からある地点までポータルを経由して移動するための時間が計算できる。

さて、よく見ると、直接向かうよりポータルを使うほうが早く着ける地点は右端からの区間になっているため、その端点を見つければ適当な前計算で一気に和が求まる。この端点のインデックスは、直接向かうときの時間を考えることで、iが増えるにつれ単調に増加するとわかる。よって尺取り法の要領でこれまた線形で求まる。N\le 10^6でTL 1secと\logを付けた解法を落としに来ている制約。最初は遅延セグメント木と二分探索を使っていたが、\log二つで当然のようにTLEしたため書き換えた。

P4は大変。一か所にしか出現できないものと端に出現するものは確定し、それ以外は出現できる範囲が区間になる。また答えとなる数列において、注目している数より大きいか小さいかだけ考えると、最初にあったところから左右それぞれ出現範囲より一つ広い位置まで、これが交互に現れなければならない。以上の条件を実装したら通った。これ以上詳しい解説を書きたくない。assertを大量に入れつつ実装し、ランダムケースでチェックすることで何とか解けた。

P5は典型。木においてすべての頂点を通る最短パスは、直径の端点を結び、直径上の辺を1回、それ以外の辺を2回通るようなものである。今回は特に、通る頂点を順に並べたものが辞書順最小となるパスを聞かれているので、まず直径の端点となれる最もインデックスが小さい頂点を求めなければならない。全方位木dpで最も遠い頂点までの距離を管理したが、よく考えると1本直径を見つけてその端点から直径と同じだけ離れた点を列挙するほうが簡単だった。

あとはdfsっぽく計算。直径上にないため後から親まで戻ることが確定しているような頂点は、記録するタイミングをある程度自由にずらしてよいので、もっと小さい番号の頂点が下にあればまずそちらに降りて行く必要がある。これを忘れて3WAした。

P6はbitsetを使った愚直を書いて終了間際に最初の部分点だけ取った。合計510点で2位、レートは2475→2666(+191)でhighest。今回は何とかなったが、やはり中盤の重実装がつらい。後ろのほうにド典型が置かれていることが多いので、詰まったときもちゃんと先の問題を確認するよう心掛けたい。しかし実際のところP4の公式解説はかなり頭が良かったので、作問者から見れば別に重実装でもなんでもなかったようだ。

しばらくインターンに関するコードを書いて、午後6時から1on1に臨んだ。今回試した方針は非常によさそう。まだ試し切れていない細部の実装について画面共有しながら少しずつ手を加えてみて、平均的にかなりうまくいっている出力が得られた。これ以上はハイパラ調整に足を踏み入れることになるが、今は出力の評価を目で見るだけで行っているし、手持ちのデータも少ないので、それは今するべきことではないという結論になった。

ということでタスクが一つ完了。次は何をすればよいかと聞いて一つ紹介され、関係するコードの在処を含めてしばらく説明を受けた。以上2時間くらいのミーティング。終盤眠くなってまたしても意識を飛ばしてしまったタイミングがあり、かなり申し訳ない。今日急に新しいタスクをくださいと言ったのがミーティングを長引かせてしまった要因。眠気に耐えられないならちゃんとそう白状するべきだった。

布団に滑り込み、午後8時半から午後11時まで仮眠を取った。起きて食事し、午後11時半からCF #819 combined。

Dashboard - Codeforces Round #819 (Div. 1 + Div. 2) and Grimoire of Code Annual Contest 2022 - Codeforces

Aは(l,r)として(1,n-1),(2,n),(1,n)のどれかしか考えなくてよい。2乗が通る制約だが、それぞれ線形時間で計算できる。Bは最大の要素以外は同じ数を偶数個ずつ用意する必要がある。ほとんどを1で埋め、nの偶奇に応じて残り一つか二つを調整する。Cは開きかっこの深さを見て、同じ深さのペアで間により浅いかっこがないものを繋ぐことになる。stackで管理。

Dは辺の数が(n-1)+3以下なので、全域木に高々3辺追加したようなグラフ。とりあえず全域木を一つ取って赤で塗り、残りの辺を青で塗ることを考える。青い辺がループになっていなければよい。ループだった場合は、青い辺uvを適当に取り赤い辺でu-vパスを作ると、そのパスは必ず長さ2以上だから、パスの適当な1辺と色を交換することでループを解消できる。3辺を乱択してループができていないかチェックする解法もあるらしい。

Eはp_i-(p^{-1})_iにおいてi\leftarrow p_iとすることで(p^2)_i-iが得られる。これは逆順列の定義から導けるし、pからn頂点n辺の有向グラフを作って(p^{-1})_i\rightarrow i\rightarrow p_iというパスを観察することでも言える。今pは順列だから、条件を\forall i.\;|(p^2)_i-i|\le 1と書き換えることができ、ここからp^2の形を観察すると各ループがi\leftrightarrow iまたはi\leftrightarrow i+1となることがわかる。

p^2がそのようなループからなるpとはどのようなものか。pにおける長さLのループはp^2において、Lが偶数の時長さL/2のループ2本に、奇数のとき長さLのループ1本になる。よってp^2における長さ1のループはもともと長さ1だったか長さ2が分裂したものであり、長さ2のループは長さ4が分裂したものである。ここまでわかれば、あとはp^2における長さ2のループの個数を全探索して組み合わせ計算を行うことで答えが求まる。

このようなp^2に関する考察は何回か見たことがあるし、特につい最近もTLで流れてきた。その時改めて自分の中で解決しておいたので内容を完璧に覚えており、今回スムーズに考察を進められた。

Fは面倒。c_i\leftarrow c_i+\sum_{j=1}^{i-1}d_jと定めることで信号間の移動時間を0にしておく。実家dpを考えると、区間[L,R)で青になる信号に突入した時、点Lにおける値は新たに計算、区間[L+1,R)の値はそのまま、残りは\inftyという更新ができればよい。同じ値を持つ区間をまとめれば、信号ごとに区間をいくつか削除して定数個足すことで表現できるため、更新が十分高速。点Lにおける値はそれ以前の\inftyでない最も近い点を選んで遷移するだけでよい。

Gは面白い。まず最初にaがすべて等しい場合を取り除いておく。これはどのような順序で操作しても条件を満たす。さて、最終的に揃える先をAとする。これはA\ge \max aを満たす。a_i\lt Aなものを抜き出して同じ値をまとめると、そのうちでb_i=1となるものは高々一つしかない。なぜなら、二つ以上ある場合、それらの間で操作後の値が食い違ってしまうから。

ここでa_i=\min aを調べると、実はAが決まる。まずb_i=0がある場合、対応するa_iは操作しても絶対に変わらないので、a_i\lt\max a\le Aより不適。よってb_i=1である必要があり、先ほど見たようにそのような要素は高々一つ。特に今回はちょうど一つであり、A=n-1+\min aとなる。あとはa_iが同じ要素をまとめ昇順に見て、操作列に挿入することを考えればよい。挿入箇所が一意に定まるのでうまくいく。A=a_iのときだけ少し式が変わる。

Hを見て諦め、残り時間はABをlockしてHackを試みていた。結局何も落とせなかったのだが、システスでRoomのAが二つ落ちていた。n=2で壊れるコードと配列外参照。後者はともかく前者はちゃんと見つけられるべきだった。

コンテスト終了直前にGの\min aに対する式が間違っているように見えて冷や汗を垂らしたが、上に書いたように答えが0にならない条件がかなり厳しいので、結果的には合っていた。

システス前6位でpredictorがすごい値を出しており、心臓に悪い。ジャッジが走っている間は他のことに手を付ける余裕もなく、YouTubeを眺めたりしてただ時が過ぎるのを待っていた。結果は全問通って7完、順位が一つ上がって5位、2954→3111(+157)。レートの更新を待ってツイートした。

2998を踏んでからほぼ一年となる。感慨深いタイミングだ。

4完15位で2952→2998(+46)。

週記(2021/09/27-2021/10/03) - kotatsugameの日記

しばらくリプライの返信をしつつ噛みしめていた。午前4時半に布団に倒れ込み、少しハーメルンを読んだところで寝落ちした。午前5時だった。

09/07(水)

午前8時半起床。昨日のCFで問題の剽窃が発覚し、Unratedになっていた。

Round #819 Unrated due to Problem Theft - Codeforces

良いタイミングだったんだけどな、とか、サークルSlackやインターン先Slackで報告したのを取り消しておかないとな、とか考えていたはず。以下に引用したツイートで述べているように、自分ではそれほど気にしているつもりもなかったが、しかしこの日は一日なんだか調子が悪かった。

剽窃があったのはF問題。両隣のEとGに比べると実装が面倒で面白くないと感じる。わざわざこの問題を選んで引っ張ってきたというのは意味不明。犯人はWriter三人のうちただ一人だけ紫だった奴で、この一問しか担当していないようだ。橙二人が用意したコンテストに質の悪い一問を追加してUnratedにするのって本当に余計なことしかしていない。確か#810で剽窃した奴も紫だったな。そもそもその程度のレートで問題を提案できるシステム自体に納得いっていない。5桁に易々届く参加者数には見合っていないだろう。

しばらくハーメルンを読んで午前11時半ごろまた寝落ちした。午後10時半起床。ずっと布団でハーメルンを読んでいて、3作読了。

syosetu.org

「真顔のシングル厨がアローラ入りするお話」。原作のアローラ地方の設定が重くてびっくりした。主人公の手持ちポケモンがどれも珍しく強力で、それらが注目を集めたりするシーンは良かった。ただヒロイン二人がどちらもヤンデレなのが好きではない。そもそもヤンデレを受け付けないから。

syosetu.org

ゲームで使っていたパーティーがたまたま手に入るところから始まる。この導入時点ではポケモンたちが主人公に対してどういうスタンスなのか、同じ主人だと認識しているのかなどわからず不穏だったが、以降は面白い。パーティーに振り回されまいとちゃんと努力してはいるものの、結局勝敗についてはポケモンが強いというのが大きいし、言動も人を煽るようなものなのでSNSが炎上気味ということで刺激的だった。

syosetu.org

多分途中まで読んだ記憶がある。少なくとも最初の2話3話には見覚えがあった。最近読んでいるポケモン二次創作とは毛色が違い、バトルシーンはあまりなくほのぼのと進む。たまにはこういうのも良い。

しばらく日記を書き、朝方になってからTCO Finalsのための提出物を作り始めた。今日は自己紹介動画の撮影。わざわざこのためにスマホアームを買ったり床屋に行ったりしたのがもう2週間も前のこと。ちゃんと原稿も用意した上で、面倒すぎて放置してしまっていた。撮影の前に英語の発音やアクセントの修正を試みたもののよくわからなかったので、諦めて日本語英語で押し通すことに。

1時間程度かけて何度か撮り直しを行い、ついに完成。ちゃんと音が入るよう大きな声で話したり、頑張って口角を上げようとしてみたり、いろいろ考えることがあってそこそこ大変だった。原稿は動画時間のことを何も考えずに作ったので、実際に撮ってから制限の15秒に見事に収まっていることに気づきびっくりした。

午前8時に布団に入った。明日のセミナーが午後1時からなので、移動時間も考えて睡眠時間4時間ちょっとの予定。ここでなぜかハーメルンを開いてしまい、気づいた時には午前10時になっていたので、今日は寝ずに大学に向かうことにした。幸い起きたのが午後10時半だったから耐えられるだろうとの見込み。

正午くらいに布団から出て準備を始めた。外は雨が降っており、川内の生協に寄るのが面倒だったので、とっとと山に登ってしまって山の上の生協で昼食を調達したい。そこは今12時45分までしか開いていないので、いつもより早めに家を出る必要があった。何とか間に合って無事総菜パンなどを購入。教室で食べた。

午後1時からセミナー。まず最初の2時間は同級生の発表だった。前回のことを比較的覚えていたので、今日はかなり集中して聞けたと思う。変数や関数の型を丁寧に確認することで添え字でもあまり混乱しない、が、その代わり本当に丁寧に見ないと厳しい。今はD_{i+1}=(D_i\rightarrow D_i)と定められた型の話が続いている。D_{i+2}=(D_{i+1}\rightarrow D_{i+1})=(D_{i+1}\rightarrow(D_i\rightarrow D_i))くらいまで展開されると勘違いしやすく、2重になったラムダ式の型で今日も引っかかってしまった。

続いて博士課程の方のセミナー。もう何度か同じ内容の説明を受けているが、今日はぐんと理解が進んだと感じる。話されていることに対する知識がついたので英語もより聞き取れるし、何より対面+黒板発表というのがいい。板書の最中に疑問が生じたらすぐさま質問できて、そのとき英語だけでなく身振り手振りでコミュニケーションが取れる。これまではスライドとオンラインが多かった。

午後6時帰宅。疲れ果てているがしばらくコードゴルフをして、午後9時くらいに仮眠に入った。ここで日記の日付も変えておこう。

09/08(木)

午後11時半起床。すぐECR135に出た。

Dashboard - Educational Codeforces Round 135 (Rated for Div. 2) - Codeforces

Aの題意が把握できず少し迷走した。最大値を取るインデックスを出力すればよい。

Bは最後の操作でx\lt p_n\le nのもとx+p_nを最大化するのが最適。これはx=n-1p_n=nで達成できる。さらにp_{n-1}=n-1とすると、p_{n-2}まででx=0とするだけで条件を満たせる。残りはn-2,n-1,\dots,1と並べればよいと思った。しかしサンプルでWA。よく見るとx\ne 0x=0が交互に現れるため、nが奇数の時は少し変える必要がある。単に先頭に1を持ってくればよい。

Cはabをそれぞれpriority_queueに入れ、大きいものから比較していく。ダメだったらfを適用して戻す。fは値を急激に小さくするので十分高速、くらいの考察はしたが、fを2回適用すると1になるというところまでは考えなかった。Dは区間dp。Aliceの操作とBobの操作を一組にして遷移を計算した。かなり面倒。prependではなくappendだと思ってしまい1WA。

Eは難しい。最初にすべて赤のほうに揃えておき、いくつ黒にするかを考えることにする。Y個だったならb_i-a_iの大きいほうからY個選んで赤から黒に切り替えるのが最適。このYをうまく全探索する問題だと思って、x_j\mid(n-Y)y_j\mid Yから考えていたが、結局別のところから解いた。

x\cdot x_j+y\cdot y_j=nの解となる整数x,yを拡張ユークリッドの互除法によって一つ求める。なければ-1。求めた解を(x_0,y_0)とすれば、すべての解はg=\gcd(x_j,y_j)として(x_0+ky_j/g,y_0-kx_j/g)と書ける。今、Yの変化に対する答えの値の変化は上に凸であるため、Y=y\cdot y_jとしては頂点に近い位置を探索すれば十分。x_jy_j/gずつしか変化しないのに注意して、定数時間で頂点の左右の点を取得することができる。

Fが全く分からなかったのでGに進んだ。mが小さいのでO(2^m)くらいかける方向で考えてみる。最初からあるランタンのpowerの決め方であって、地点の特定の部分集合のみを照らせるようなものが数え上げられると嬉しいだろうか。結局答えを求めるときにもう一度O(2^m)かける必要がありそうに見えるが、あらかじめゼータ変換か何かしておけばO(m)にできる。新しいランタンのpowerを適当に決めた時、それによって照らせる地点の集合がO(m)通りしか存在せず、それぞれに対応する場合の数がこの前計算で求まっているから。

あとは最初の決め方の計算。条件を緩めて数え上げた後、包除原理でピッタリの値を求めることにする。「この地点は絶対に照らさない」という集合を決め打って数え上げることができた。照らさないと決めた地点であって隣接するものを取ると、その間にある各ランタンのpowerの上限が決まる。よってそれらを掛け合わせればよい。地点のペアすべてについてその間のランタンの分だけあらかじめ計算しておくと、この部分はO(m2^m)で計算できる。包除原理についてもメビウス変換で書けた。ゼータ変換かもしれないが、とにかくその類の計算が適用できることだけが重要。

Fに戻った。結局最小重み二部マッチングになりそう。マッチング先の頂点は必要になったら生成することにすればよい。しかし頑張って実装したものの合わず、コンテスト終了。方針としては合っているようだったので、マッチングをずらすときマッチ先の頂点の値がずらす前より増える一方だと思い込んだのが間違っていたのだろう。結果はF以外の6完で9位。

DはBobが絶対に勝てないらしい。正直これも証明がつけられていないが、さらに解説のコメント欄ではDrawとなる文字列がA+B+{\rm rev}(A)、ただしBは先頭から2文字ずつ同じ文字が現れる、という形に限ることまで議論されていた。

Eはx_jy_jのうち大きいほうで探索することで通せるらしい。このときクエリごとにO(n/\max(x_j,y_j))かかる。\max(x_j,y_j)=kとなるペアがc_k個あるとすれば、重複を取り除いてc_k\le 2k-1が言えて、さらに\sum_k c_k=mでもある。この元でkが小さいものをたくさん集めると\sum_k n/k\times c_kが最大化され、最悪ケースになる。そのときk\le\sqrt mではc_k=2k-1、以降はc_k=0となるから、計算量はO\left(\sum_k n/k\times c_k\right)=O\left(\sum_{k=1}^{\sqrt m}n/k\times k\right)=O(n\sqrt m)で確かに間に合っている。

TCO Finalsに向けた提出物は、最後にアンケートが残っている。09/09締め切り。とりあえず質問をザッと確認してみて、回答必須のものが思ったより少ないことに安堵した。しかし最後に自分の写真を添付する必要があってびっくり。明日ホスフィンと旅行に行く予定なので、旅先で撮ってくることにしよう。

旅行のために今日は早く寝なければならないと思い、午前3時頃には布団に入った。しかしそこから読み始めたハーメルンが章のクライマックスで非常に面白く、結局就寝は午前5時過ぎになった。

09/09(金)

午前7時、執念の起床。軽く持ち物を用意して出発。

今日は青春18切符で平泉に行く。仙台で暮らして5年目になるが、いつも新幹線と地下鉄にしか乗らないので、初めて仙台駅の在来線ホームに立ち入り感動した。途中の駅で宮沢賢治作品モチーフのステンドしおり2種類を購入しつつ、午前10時半ごろに平泉駅に到着した。

すぐ横のレンタサイクルで電動自転車を借り、今日は一日これで動き回る。初めて電動自転車に乗ったがかなり快適だった。金鶏山周辺の道は坂ばかりだったし、達谷窟毘沙門堂はそれなりに遠かったので、たった100円をケチって普通の自転車を選ぶようなことはしなくて大正解。

まず中尊寺に向かった。参道が急でかなり恐怖感があった。ここでは本堂や金色堂の他にもありとあらゆるお堂を練り歩き、昼過ぎまで過ごした。本堂本尊の写真を貼っておこう。金色堂をはじめ結構な数のお堂は撮影禁止だったが、これは特に何も書かれていなかった。調べた感じ、最近作られた仏像だからという理由がありそう。

あとは、お堂にペタペタ貼られているお札が千社札と呼ばれることを知り、それにも注目していた。これまたほとんどのお堂では貼り付け禁止になっている中、特に書かれていないお堂もあって、そこの柱にはお札だけでなく筆やペンによるサインもたくさんあった。平成終わりごろの日付のものがいくつもあったが、令和に入ってからのものは見つけられなかった。

金色堂覆堂の前でホスフィンに写真を撮ってもらった。アングルも含め昔よく読んでいた本に描かれていた景色で、非常に既視感がある。ただ絵面がパッとしない。そもそも中尊寺は撮影可能な場所も少ないし、写真映えを求めるスポットではなかった。

日本にのこるなぞのミイラ | 株式会社 理論社 | おとながこどもにかえる本、こどもがおとなにそだつ本

下山して駐車場の周りの食事処で昼食。次は毛越寺に行く予定で、途中に金鶏山があるので寄ってみた。しかしここはシンボルとして有名なだけで、登っても何も面白いものはない。麓に廃寺があったらしいが見逃し、急な山道を必死に歩いて山頂に着いてみたら、ただ塚があるだけで周りは鬱蒼としており見通しもよくない。意気消沈して下りた。足を滑らせたらそのまま転がっていきそうなほどの勾配で、登るより下りるほうが格段に怖かった。

毛越寺。「夏草や 兵どもが 夢の跡」という有名な句の、新渡戸稲造による英訳の碑があった。「The summer grass / 'Tis all that's left / Of ancient warriors' dreams.」。'Tisという単語を知らず、調べてIt isの省略形であることを知った。庭園の池をぐるっと一回りして、甘味処でかき氷を食べて出た。

次は20分ちょっと自転車を漕ぎ、達谷窟毘沙門堂へ。ここはフォトジェニックでよかったので、またホスフィンに写真を撮ってもらった。自分でも撮ったので一枚貼っておこう。サムネ用。全体を収めるためには横向きで撮りたくなるが、これだと画面の上まで崖で覆われて窮屈さを強く感じる。そこで縦向きで撮ると、せり出した崖の上の部分まで見えて、圧迫感が自然の雄大さにすり替わってくれる、気がする。

本当は温泉に入る予定だったが、レンタサイクルの閉店時間に追われながらは嬉しくないので、いっそ帰ってから銭湯に行こうということになった。電車に乗って仙台へ。乗り換えで立ち寄った一ノ関駅ピカチュウポケモントレインの顔出し看板があって、そこでもホスフィンに写真を撮ってもらった。以上3種類のどれかを選んでアンケートに添付しよう。

今日の行き帰りの電車では昨日のハーメルンを続けて読んでいた。1作読了。「自分はかつて主人公だった」。

syosetu.org

主人公自身が強いし、手持ちポケモンが伝説ばっかりで最高。かつての力やパーティーを取り戻すという展開で、序盤の少し弱くなった主人公描写に耐えたぶんクライマックスは非常に興奮した。ストーリーとしても面白い。人間的成長という一見ふわふわした話が主人公の実力とダイレクトに連動している。だから昨日睡眠時間を削ってでも一気読みしたし、今日も電車の中で寝るつもりがずっと読んでしまったのだ。

午後6時半ごろ仙台駅に到着。たいぺーとも合流して地下鉄に乗り、サンピアの湯というスーパー銭湯に向かった。久しぶりの大きな風呂で非常に良い。サウナに入った後水風呂に浸かったら、今日一日ずっとあった眠気が一瞬だけ完全に吹き飛んで非常に爽快だった。その分風呂から出て併設のレストランで食事しているときには一気に眠気が来て大変だった。

その後寝転べる場所に移動して2時間ほど漫画を読んでいた。虚構推理の1巻と、転スラの10巻から15巻あたり。前者は小説版を積んでおり、序盤を漫画で確認することで読む意欲を高めようとした。無事高まったものの本当に小説版を読むかは不明。後者については、神之怒のシーンが大好きなので探していた。無事見つかってよかった。

午後11時過ぎに銭湯を出て、日付が変わったあたりで帰宅した。また少し汗をかいてしまったのでシャワーを浴び、布団へ。旅行で撮ってきた写真を一度にツイートした。

鐘楼の床に穴を開けるのは何故だろうか。音の響きをよくするためという理由が思いつくが、調べてもヒットせずよくわからないままである。

午前1時就寝。

09/10(土)

午前7時半起床。ABC264の賞金が届いていた。

少しハーメルンを読んで午前9時過ぎに布団を脱出。睡眠時間が足りていないので、夕方また仮眠するつもりである。

しばらくコードゴルフした後、昼頃にTCO Finalsのアンケートを書いた。写真は一ノ関駅で撮ったものを使った。提出時刻はアメリカのタイムゾーンではギリギリ09/09なので、締め切りに間に合ったと言えるだろう。このためにわざわざ朝から起きていたのだ。

布団に戻って仮眠の体勢に。しかしダラダラハーメルンを読んでしまって、結局寝入ったのは午後4時前だった。次に午後8時起床。あと30分くらい眠れるかなと思ったのにTLを見ていたら寝損ねた。

最初から午後8時半に目覚ましを設定するのは怖いのに、午後8時からもう30分寝るのが怖くないのは一体なぜだろうか。恐らく後者は、その時の眠気が強すぎてとにかく寝たいという気持ちでいっぱいなのだろう。実際このせいで寝過ごしたことが何度かある。

食事して午後9時からABC268に出た。

UNIQUE VISION Programming Contest 2022 Summer (AtCoder Beginner Contest 268) - AtCoder

AはRaku。Bはsed。Cは少し難しい。テーブルがk/N周回っているとき喜ぶのは(p_i-i-k)\bmod N-1,0,+1であるような人iだから、(p_i-i)\bmod Nをカウントしておくことでkごとに人数が定数時間で求まる。

Dはdfsで全探索。計算量を考えずに提出したら、そもそもNMを取り違えていてTLEやREを出してしまい、もしかして間に合わないのかと震え上がった。バグに気づいて提出しなおし、WA。|X|\ge 3をチェックし忘れていた。この条件いる?

Eは気合いで差分更新。Fは各Sについてそれだけ考えた時のスコア、数字の和、Xの個数の三つを計算すると、これらの組み合わせで全体のスコアが計算できる。式を見ることで、二つのSの結合順序を入れ替えたときにスコアが上がる条件が求まる。ちゃんと順序になっているので、それを比較関数にしてSをソートすればよい。

Gは各Sに対する答えが、i=1\dots NについてS_iが辞書順でS以下となる確率を足し合わせたものになる。S_iSのどちらかがもう一方の接頭辞となる場合は関係が確定するので、それ以外を考えたい。これは先頭から見て初めて食い違う位置の文字の順序における大小関係から定まって、冷静になると大小どちらも26!/2通りずつである。なので最初に弾いた接頭辞の部分だけ真面目に計算すれば十分で、Trie木で求まる。

Exはよくわからない。そのまま残してはいけない区間を列挙できれば、貪欲法で答えが求まる。その区間自体もSTたちを連結した文字列のSuffix ArrayとLCP Arrayを使うことで計算できそうだが、自分が考えた方法では各Tに対してSの位置を見ていたので、STも全部同じ文字というような最悪ケースでは間に合わなさそうだった。そこでGのコードを流用して、別のTが接頭辞となっているものを無視することにした。これだととりあえず先ほどのケースはうまくいくし、考えてもそうひどいケースが見つからない。信じて提出したら1100msくらいで通った。

全完18位。AのFAだった。Dの計算時間は、そもそもM+1個候補を生成すれば必ず見つかるし、全通り生成しても十分高速らしい。GはPとしてa-zz-aの二通りのみを考え、その平均を取ったものが答えになるようだ。言われてみればそう。ExはSの位置に対してT、特に|T|が最短のものを探すと全体で線形時間になるため、接頭辞云々で枝刈りする必要はなかった。

コードゴルフについて。これは以降二つのコンテストの前後でやっていた結果も含む。AはFAだったRakuのコードがそのまま最短。BはsedよりVimのほうが4Bも短くてひっくり返ってしまった。CはPerl。Fは数字の和とXの個数を複素数の形で持つと偏角ソートしたものが求める順序になって、かなり縮んでくれた。Gは上で触れた、二通りのPに対して計算する方法。他は手付かず。

午前0時からSRM837。

https://community.topcoder.com/stat?c=round_overview&er=5&rd=19397

最初15分くらいエラーで提出できず、Unratedになった。普段と違う時間にコンテストを開催してエラーが起こるというのは昔AtCoderでもあった記憶がある。その時はジャッジサーバが起動しておらず、序盤の提出を捌けていなかったはず。同じ原因かはともかく、これは同情できる。ただしUnratedの理由が「As the disruption is taking longer than we hoped」だったのは信じられない。もしかして数分で復旧したら続行するつもりだったのか?

そういうことがありながらもちゃんと全問解いておいた。EasyはHの昇順にソートして使う。W_i\times H_i\gt 1という制約が何なのか不安になったが、通常のcubeと同じサイズのブロックがあると、それを他のブロックの上に置けないかどうかはどう決めたってややこしいので、避けた形だろうか。

Medはdp。バケットごとに使うバックアップの個数を決め打つと、できるだけ偏らないように分割するべきなので、かかるコストが閉じた式で計算できる。状態数O(HT)、遷移がO(T)なので十分間に合う。バックアップは使い切るべきだと思っていたが、念のため何個使うか全探索して答えとした。実はバックアップを一切使わないケースというのもあるらしく、それで落ちていた人がいた。

Hardは気合い。文字列のインデックスと、そこまでに使ったupperBoundsの集合を持ってdpする。遷移は特に工夫せず丁寧にチェックするだけでよい。問題はgoodとbadの区別。ある分割がgoodであるとは、分割後に隣り合う二つの文字列a,ba+b\ge b+aを満たすことである。なので、dpするとともにgoodに対してそれを達成するaを列挙しておき、新しくbを足したときgoodのままかbadになるかチェックするという方法を考えた。特に上の比較はちゃんと順序になっているので、aとしては最小と最大しか保存しなくてよい。

チャレンジフェーズは何もせず。システスは無事全部通って全完5位となった。

午前2時からMHC R1が始まったので出た。24時間で終了するのでここに感想を書く。

Meta Hacker Cup - 2022 - Round 1

A1は簡単、どれくらいrotateしなければならないか決まるのでK=0に気を付けつつチェック……と思ったらvalidationで落ちた。冷静になるといろいろコーナーケースがある。まずK=0の場合rotateできないのはサンプルにもある。次にK=1のとき、これは逆に必ずrotateしなければならないので、最初からA=Bだった場合はダメ。

以降K\ge 2として、rotateできる範囲が[K,(N-1)K]なので、(N-1)K-K+1\ge Nならどんな状態も達成できる。式変形するとN\ge\frac{2K-1}{K-1}=2+\frac 1{K-1}\gt 2、つまりN\ge 3ならOKだがN=2はコーナーとなっている。実際このとき、rotateする幅とKの偶奇が等しい必要がある。以上を実装して提出。A2はB+A+Aに対してZ-algorithmを適用することでrotate幅を全探索できる。

Bはいつもの。縦横分割し適当にソートすれば求まる。B1とB2で同じコードを出した。Cはしばらく考えても糸口がつかめなかったので諦めた。

ハーメルンを1作読了。「東方神殺伝~八雲紫の師~【リメイク】」。

syosetu.org

「俺の家が幻想郷」の作者の別作品。中二臭さがかなり好みだったのでこちらも好きだろうと思って読み始めたが、実際かなり良かった。原作キャラより圧倒的に強いオリキャラたち、オリジナル能力、二つ名……。一方、せっかく幻想郷にいるのにオリキャラばかり出てきて原作キャラの出番がどんどん減ってしまったので、それはちょっと好みから外れていた。リメイク前はオリキャラたちのインフレがもっと激しかったらしいのでそちらも少し気になる。

朝からは日記を書いていた。午後2時くらいに切り上げて布団に入り、1時間ちょっとラノベを読んでから寝た。

09/11(日)

午後8時半起床。食事して午後9時からARC148に出た。

AtCoder Regular Contest 148 - AtCoder

AはM=2で多くとも2通りが達成できるので、1通りが達成できるか調べる問題になる。つまりA_i\bmod Mがすべて同じ値になればよく、ここからM\mid(A_i-A_j)が出る。よってM\mid\gcd(A_2-A_1,A_3-A_2,\dots,A_N-A_{N-1})で、\gcd\pm 1かどうかを調べればよい。0になりうることを見落として1WA。

BはSにおける最初のpの出現位置からスタートする部分文字列をひっくり返すのが最適なので、候補がO(N)通りになって全探索できる。そもそもpが出現しないならひっくり返さなくてよい。

Cは最初、クエリごとに必要な頂点だけ取り出して圧縮した木を作ることを考えたが、あまりに面倒だったのでもうちょっと考察してみたらスマートに解けた。ある頂点のコインをひっくり返すときは、まずその頂点のボタンを押す必要がある。するとその子以下もすべてひっくり返ってしまうので、特にひっくり返したくない子があった場合はそれらのボタンも押す必要がある。逆にひっくり返したかった子のボタンを押す手間を省いたとも見なせる。

これをまとめると、クエリiの答えは、まずv\in S_iに対してvvの子の個数を求めて和を取り、そこからv\in S_iであってその親uu\in S_iを満たすようなvごとに2だけ引いたものとなる。

Dは、まずAに同じ値が二つ含まれていた場合、それらを取り除いてよいことがわかる。この操作をした後にAの要素が残るか残らないか見れば答えになると思い、提出。WA。もうちょっと真面目に考える。二人の和がそれぞれX,Yで、黒板に書かれた数の残りがx,yであるとする。この下でBobが勝つ場合、X+x\equiv Y+y\pmod MかつX+y\equiv Y+x\pmod Mである。式変形することで2(x-y)\equiv 0\pmod Mがわかるため、Mが偶数の時はM\leftarrow M/2として上で述べたことをチェックするのがよいと思った。またWA。

よく考えると、2(x-y)\equiv 0\pmod Mかつx-y\not\equiv 0\pmod Mなる(x,y)は偶数個使わないと和が一致してくれない。よって、最初に同じ値二つを削除するのを繰り返した後、残った個数が4の倍数で、かつ\bmod{M/2}で一致する値のペアを消していくと全部消えるというのが条件だと考えた。出したら通った。

Eは難しかった。Aの要素は最初全部区別しておいて、後から適当に割ることにする。列にどんどん挿入していくことを考える。Aの要素を昇順に見るのも降順に見るのもうまくいかなかったが、K/2で分割するとうまくいった。

列の両端にKが置かれているとみなして、新たに要素を配置できる隙間を管理しつつ、K/2未満の数を昇順に見る。a\lt K/2を見ている時点でa未満とK-a以上の要素がすべて配置済みとなるようにしておく。隙間を一か所選んでaを配置すると、その両隣には残ったK-a未満の要素が配置できないから、単に隙間を一つ減らすという効果になる。aを配置する前にK-a以上の要素を使い切るときは、隙間を一か所選んで挿入すると、その両隣は構成から必ずK-a以上となっているため、新たに隙間が二か所できる。

aを次の数に進めても、挿入できる位置は増えも減りもしない。こうやって隙間の数を簡単に管理できることが重要で、これを満たすためにK/2で分割して昇順と降順を分けている。あとは毎回の挿入位置を考えて組み合わせ計算するだけ。ところが2REしてしまった。combinationのライブラリは引数に符号なし整数を取るように作ったので、隙間の数がうっかり負になったとき、バカデカい値を計算しようとしてしまう。

1時間残してFへ。得意そうな見た目をしていたが、結局解けなかった。モンゴメリ乗算にはたどり着いたものの、リダクションの最後、if文による調整の部分が書けない。結果は5完5ペナで13位。ペナルティが多すぎる。Eは冷静になると隙間の数を答えに掛けながらインクリメント・デクリメントするだけでよく、わざわざライブラリを持ち出す必要がなかった。

コードゴルフ。AはRakuが間に合う。ずらして隣接項の差を取るのではなく全部の項からA_1を引くと高速になったし、[gcd]というreduce用メタ演算子の性質が効いて縮んだ。

もともとgcd演算子は正の数を返すようなので、これが1であるか判定するだけでよいと思っていた。$a gcd $b gcd $cという式が[gcd] $a,$b,$cと書けることを利用して全体の\gcdを求め、判定。ところが[gcd] $aと1要素しかない場合、メタ演算子の仕様上$aがそのまま返ってきてしまう。$aがちょうど-1だった場合にはうまく判定できないのだ。全部の項からA_1を引く方法だと必ず2要素以上になるので、正しく正の数が返ってくる。

BはPerl。ひっくり返す部分文字列が先頭のpの後に何文字続いているかを全探索し、正規表現で検出した。最も先頭の出現にマッチするので正しく動く。最初はわざわざpのインデックスを求めてsubstrで切り出していたが、この方法でかなり短くなった。以降は手付かず。

午前2時にMHC R1が終了した。提出したA1A2B1B2は全部通って76位、スコアが十分あるので当然通過。Cは座標の範囲が[0,a)\times[0,a)に制限されていると格子点からなる凸包にはO(a^{2/3})点しか乗らないらしい。言われてみれば昔聞いたことがあるかもしれないが、すっかり忘れていた。それでもV=35000頂点のdijkstraを解くことになる。これはpriority_queueを使わない方法でO(V^2)が達成できるので、確かに間に合いそう。

朝まで日記を書いていたが、急激に眠くなったので布団に移動。午前7時就寝。

週記(2022/08/29-2022/09/04)

08/29(月)

午後3時起床。11時間くらい寝たらしいが体感としてまだ疲れは残っている。TLを眺めていたら二度寝するタイミングを逃し、ノソノソ起き上がって午後4時半からインターン先定例会。

先週の進捗はまあまあ。金曜日の1on1で話したことがすべてで、日記でも至る所が崩壊したと書いたように完成に近づいているわけではないが、いくつか方法を試してダメだということが分かったのは結構重要だと思っている。なぜダメだったのかの考察も少し加えつつ、1on1で提案された別のアプローチを試すのを今週やりますと言っておいた。

勉強会は線形空間とか線形写像についての話。序盤は知っていることだらけなのでよい、と気を抜いていたら、終盤難易度が急激に上昇し、知らないものがよくわからないまま終わってしまった。極限から位相を定められるということを知ったのが収穫といえば収穫だろうか。

定例会を終え、今日も先週の週記を書く。オンサイト参加記の下書きとして土日の行動記録をつけていたので、それの日曜日の分を完成させた後、週記のほうに手を付けた。こちらは金曜日の分から残っていた。土日の日記はつけておいた行動記録の内容を参加記とで振り分けるつもりなので、同時並行で書き進める必要がある。

とりあえず土曜日のコンテスト、ABCとECRの部分を書いた段階で日付が変わりそうになったので、投稿した。これまで未完成の状態で投稿したことは何度もあるが、今回は特にボロボロ。日曜日の部分を全く書けていない。

どういう形であれ何とか月曜日中に投稿できたので、気が抜けてしばらくYouTubeを見ていた。

午前2時にMHC2022 qualが終わった。全完14位。順位にあまり意味がない形式のコンテストだが、それでも順位表の最上位層に位置できたのは素直にうれしい。

Meta Hacker Cup - 2022 - Qualification Round

Aは難読気味に感じられた。読めればやるだけ。B1は全部木で埋める、B2は木を置いてはいけないマスをBFSのように列挙して、そうでないマスを木で埋めればよい。Cは先頭文字を与えられた文字列と異なるものにして、以降の幅を固定しインデックスの2進数表現を埋め込んだ。C2の制約も満たせる解法。むしろC1だけ満たす構築は不自然ではないだろうか。

Dは少し面白い。クエリ(X,Y)に対しては、X\rightarrow u\rightarrow Yと飛べるフライトのペアを列挙しCの小さいほうを答えに足せばだいたいOK。X\rightarrow Yというフライトは朝夕2回使えるが、これは別に処理してもよいし、任意の頂点uについて定員10^9人のu\rightarrow uというフライトを足せば、最初に計算したフライトのペアと同様に扱える。後者の方法で実装した。

フライトを重み付きグラフとして、クエリ(X,Y)についてXの次数がYの次数以上であるようにswapしておく。Xが同じクエリを集め、まずX\rightarrow uという辺すべてを見て、uに乗客数を保存する。次に、XとペアになったYに対しu\rightarrow Yという辺をすべて見て答えを求める。このとき(X,Y)が全く同じクエリが以前にあったならその答えを使う。

計算量を解析する。まずXに接続する辺を見るのが全体でM回。Yについては次数が自分以上であるような点が何個あるかが問題になって、最悪ケースはクエリに出現する全頂点の次数が等しくなる場合のはず。その次数をcとすると、XまたはYとして出現するのがM/c頂点なので、ペア(X,Y)(M/c)^2種類。またこの値はクエリQ個でも押さえられるため、都合c\times\min((M/c)^2,Q)回くらい見ることになる。この値はM^2/c=Qcつまりc=M/\sqrt Qのとき最大値M\sqrt Qを取る。よって間に合う、だろう。

手元で確か50sec弱かかったはず。うっかりただのリダイレクトで実行してしまったため出力が見えず、いつ終わるのか気が気ではなかった。実行時間が長いと予想される場合、ちゃんとteeコマンドを使って途中経過を眺めるようにしたい。

Dの計算量についてより正確な証明をへのkのツイートで知った。

先週の週記について、金曜日の分まで校閲を済ませた。問題の解説として書いていた文章が盛大に間違っていてびっくりした。Codechef 8月LunchtimeのMEXPERMDIFについてで、順列の転倒数とKが一致すると書いていたが全くそんなことはない。特殊な形をした順列、つまり「転倒数がKになるような順列を構築せよ」と言われて先頭の項から調整した、辞書順最大っぽい順列だった場合はこれが成り立ちそう。正確に書くとまた長くなるので言葉を濁しておいた。

PN=0PN=0 のときPPの転倒数を減らしていくような感じでB=KB=Kを11ずつデクリメントしていける

週記(2022/08/22-2022/08/28) - kotatsugameの日記

ハーメルンの更新を読んだ。1か月に1回ペースでしか更新されないのにしばらく主人公が暗躍するばかりで登場しなかったので、最新話で久しぶりに出番があって非常に嬉しかった。焦らしただけある格好いい登場シーン。

syosetu.org

布団に入ってハーメルンを1作読み始めた。午前8時半就寝。

08/30(火)

午後6時過ぎ起床。

今朝読み始めたハーメルンを放棄した。「役満で上がると惚れられる」。咲のR18二次創作。主人公は、少なくとも序盤においては特に麻雀で強いというわけでもなさそうだったので、読む気力が続かなかった。

ハーメルン - SS・小説投稿サイト-

起きて参加記の続きを書こうとしたが、どうにも内容が思いつかない。そもそも参加記という形式のブログを書くことが久しぶりすぎて、詳細に残しておいた行動記録のどれを参加記に、どれを日記に書くべきか決めきれない。集中が切れてかなり長い時間をYouTubeに費やしてしまった。

午後11時50分からCF #817 div.4。15分こどふぉってこの時間になった。

Dashboard - Codeforces Round #817 (Div. 4) - Codeforces

Aはソートして判定。BはGBに変えて一致判定。Cはmapで文字列の出現回数を数えた。それぞれの人は同じ文字列を書かないので実装はかなり簡単。O(nt)回のmapアクセスも、文字列長が3なので速度的に問題ない。Dは変更することによる増分を列挙して大きな方から使う。

Eはちょっと時間がかかった。二次元累積和を使うと計算ステップ数が10^8くらいになるが、TLが6secということもあって十分間に合いそうではある。ただこのことを信じ切れず、BITを使ってO( (n+q)(\log(n+q)+\log\max w))で解いた。まず、二次元累積和と同様にしてクエリを四つに分解する。それぞれの形式は、(h,w)に対して、h_i\lt h\land w_i\lt wなるiについて\sum_i h_i\cdot w_iを求めるものである。あとは(h_i,w_i)とまとめてhの昇順にソートし、wをBITのインデックスにして管理すればよい。

Fは面倒なだけ。Gは全要素のXORが0であればよくて、n\bmod 4の値で場合分けした。ちゃんと4k+0,\dots,4k+3の四つ組を作らないとXORを0にできないのに、連続する四つの数であればよいと早とちりしたまま提出。サンプル1で落ちたためペナルティがつかなかったのは幸い。

36分全完で17位。EFGでそれぞれ時間をかけてしまったのが痛い。Eを改めて二次元累積和で書いてみたところ500msもかからず通ってしまった。そうか……。

TCO Finalsのフライトがまた提案されていた。羽田空港発着となるようお願いしていて、とりあえず行きは問題なく見つかったらしい。ただ帰りの飛行機が希望する時間帯に見当たらず、どうしても羽田空港に帰って来たいならボストン出発が朝になり、そうでないなら正午出発で成田空港着となるとのこと。

成田着のフライトの日程が添付されていたので見てみると、ボストンを土曜正午に発ってそのまま成田までの直行便、到着が日曜午後4時過ぎだった。両方現地時間である。実はこれは非常にありがたい。ボストンは日本より13時間遅れているため、出発が日本時間で日曜午前1時になる。すると、土日どちらも日本時間午後9時から数時間は陸地にいることになって、AtCoderのコンテストに問題なく参加できるのだ。これほどピッタリな便はそうそうないだろう。成田空港着でも構わないからこれを予約してくれ、と伝えた。

そういえば9月からSlackの過去ログを見られる範囲が変わるらしいので、今のうちにサークルで使っているワークスペースのデータをエクスポートしてみた。publicなチャンネルだけなので、例えばICPCチームで使ってきたprivateチャンネルの内容はこのままデータの藻屑と消えてしまう。残念。12000件ほどあるのに一瞬で完了してびっくりした。Windows機でデータを解凍するとフォルダ名が文字化けしてしまったので、Ubuntu機で解凍して、サークルで使っているGoogleドライブに投げておいた。

改めて参加記を書いていたが相変わらず筆が乗らない。そのうちラノベに逃げて、1冊読了した。「異世界でチート能力を手にした俺は、現実世界をも無双する」11巻。

やはり現代パートが好みで、文化祭だったり生配信に映り込んでバズったりとニヤニヤできる展開が盛りだくさんだった。これが前半の内容。後半は異世界パートで、前の巻の内容をあまり覚えていなかったからよくわからなかったがとにかく無双している。主人公の過去にまつわる設定が少し明らかにされた。これまで伏線が張られてきたわけではないように記憶しているので、どちらかというと「設定が追加された」と言うほうが近かったりするのだろうか。後書きから推測するにこの巻もまた行き当たりばったりで書いているらしい。それで物語的に破綻しないのは凄まじい。

インターン先が巨額の資金調達を実施したとのことで、Slackで通知が回ってきた。すごい話だ。僕がインターンを開始してからちょうど1年経ち、その間だけ見ていてもこの会社は加速度的に成長しているように思う。それは業績云々という難しい観点ではなくもっと単純なところで、毎月社員がどんどん増えているのだ。正直適当に決めたインターン先だったが、今は愛着みたいなものもある。

午前11時就寝。

08/31(水)

午後7時起床。寝足りない気持ちを抱えながら布団から這い出た。今日は午後8時という変な時間からSRM836がある。レジって参加した。

https://community.topcoder.com/stat?c=round_overview&er=5&rd=19394

Easyはカス。ふんふん言いながら書いてサンプルを試したところでA=0のケースに気づきキレそうになった。あまりにも嫌らしすぎる。そのうえ領域一杯にしか長方形がない場合など不必要なコーナーケースが盛りだくさん。

Medもカス。見るからに嫌な形式をしている。こういう問題をサンプルだけで解かせるのはもう本当に時代遅れだから一刻も早く考え直してほしい。SRMらしさなど求めていない。頼むからまともなコンテストを運営することだけに専念してくれ。

どの文字をどのように変えるか先に決め打っておき、貪欲に操作した。変えるべき位置が1か所しかない場合はそのままだと操作列が作れないので、追加で一か所無駄な変更をする必要がある。変えなくてよい位置のみを見て、a以外の文字があったらそのうち最初の出現をaに変えて戻すのがよい。なければ最後のabに変えて戻す。

貪欲に操作するとき、残りの操作回数がちょうど2になる瞬間だけ気を付ける必要がある。同じ位置の操作が2回残っていると操作列が作れなくなってしまうので、二か所で1回ずつ残すようにする。それ以外は特に気にしなくてよい。なぜなら同じ位置で操作する回数は高々2回だから。

コンテスト後に文字列間の距離を返す関数を作っておく方法を聞き、感動した。自分の解法もシンプルに書けるようにしばらく考えて編み出したものだが、こちらのほうがかなり簡単になりそう。

Hardはまともか。まずR\times Cの盤面に辺同士が接しないようにキューブを置く。市松模様を考えるとN\le\lceil RC/2\rceilの場合はこれだけで達成できて、最適。そうでない場合、置いた\lceil RC/2\rceil個のキューブの上に余ったキューブを積み重ねるべきで、これはただの重複組み合わせ。

盤面に直接置く部分はbitDPで計算した。本当は1個ずつ置くことで計算量が削減できるらしいが、1行ずつ状態を決めても間に合った。そもそもキューブが隣り合わないような置き方がC=141000通りもなく、その2乗にRとばらまいた個数\lceil RC/2\rceilをかけて、計算ステップ数は1.4\times 10^9くらいになる。テストすると1sec強だった。

15分ほど余らせて全問提出した。チャレンジフェーズではEasyを見ていて、成功1失敗1。A=0のケースで共通部分を持たないように長方形を置くところが間違っているコードを落としたはず。もう一つ、自分が読んだ限り同様のケースで落ちると思ったコードがあったのに、なぜか落ちなくて失敗。コードを書き写すか迷っているうちに別の人に落とされた。

そのうち自分のEasyも落とされた。A=0のケースに気づいて場合分けを付け加えたのだが、その前に与えられた長方形が共通部分を持たない場合空のvectorを返すif文を書いていたのがダメ。先にA=0の場合分けを書いておく必要があった。改めてRoomの提出を見ると、EasyとMedがどちらもボロボロで、そんな中たった+25ptしかできていないのは残念。

システスでHardも落ちた。1完19位で2977→2887(-90)。ゴミコンテスト。出る価値なし。Hardのミスは盤面に直接置く部分で、R\times Cの盤面に\lceil RC/2\rceil個敷き詰める方法は市松模様とそれを反転した高々2通りしかないと思っていたら、例えば(R,C)=(1,4)などで落ちてしまった。これは間に2マス開ける方法を含めて3通りある。ちゃんとbitDPで求めた値を参照するべきだったらしい。

絶望してしばらくぼんやりとYouTubeを眺めていた。日付が変わってから明日のセミナー発表の準備を始めた。いつものように教科書を読んで内容を紙にメモ。以前準備した分が微妙に残っていたので量的にはそれほどでもないはずなのに、集中が切れまくってスマホを触ったりTLを見たり、なかなか進みはよくなかった。あとは内容も難しい。証明が回っていることはわかるが、なぜこのような方法を思いついたのか全くわからず苦労した。少しでも「お気持ち」が汲み取れていることを願う。

ひとまずの完成を見て午前8時くらいに布団に入る。ハーメルンの更新を少し読むのと、あとはチュウニズムのアップデートで15が2譜面追加されたので、その確認動画を見ていた。

どちらの譜面も一部が以下のように事前に公開されていた。この紫色のノーツの下には別のノーツが隠れていて、視認性が悪すぎるだろと思っていたのだが、その前に紫色のノーツなしで同様の配置があり、ちゃんと誘導になっていたので感心した。

午前9時就寝。

09/01(木)

正午起床。起きるに起きられず布団に突っ伏してTLを見ていたら、atgolferの更新が流れてきた。AWKで新手が出たらしい。一気に覚醒し跳ね起きて、適用できるコードを探し、10個以上縮めた。

atcoder.jp

RS=FSとして空白区切りでレコードを読み込むテクニックがある。この状態で入力の終端を検出しようとするとき、ENDブロックを使ったりNRを見たりするよりも短い方法があって、それが/\n/だった。今、空白をレコードの区切りとしているため、逆に普段のレコード区切りである改行文字が$0に残ったままになっており、それをチェックすることで行の末尾であることを検出できる。

ここで特殊変数RTを使うと短くなるようだ。そもそもそのような変数があることすら知らなかった。RTには今見ているレコードを実際に区切った文字列が入っている。後ろに入力が続いている場合、この文字列は空白、つまり真となる。一方今見ているレコードが入力の最後だった場合、そもそもレコードは区切られておらず、RTは空文字列つまり偽となる。これで判定できる。/\n/と比べて-1B。

副次的な効果でさらに縮んでいる。/\n/で判定する場合、$0を書き換えると壊れてしまうため、/\n/が真であることを確認してから$0に代入する必要があった。今RTを使う場合そのあたりのことは気にしないでいいので、とりあえず答えとなりうる値を$0に放り込んでおいてRTが偽のときのみ出力する、という方法が取れて、追加で大体-2Bくらいされる。

そんなことをしていたら午後1時になってしまった。なんと今日のセミナーは午後1時開始の予定である。先生に遅れるとのメールを送ってから急いで家を出た。途中購買で食べるものをサッと買い、何とか15分くらいで山の上の教室にたどり着いた。

今日のセミナーは、まず博士課程の人の報告を聞いてから、自分の発表。報告のほうは相変わらず知らないことだらけでよくわからない。自分の発表については、自分では別に難しい内容を喋っているつもりもないし至って普段通りに進められたと思ったのに、あまり伝わらなかったし、先生から「内容がまとまっていないように感じる」と言われてしまった。何が悪かったのかあまりよくわからないため改善が難しい。一か所、言い忘れていたことを後から口頭で補足した場面があって、それはピンポイントで不親切だと指摘されたので、とりあえずここを直すべきか。

午後4時過ぎに解散。帰宅して、まず8月の読書記録をツイートした。先月も全然ダメ。リアル読書に限らず全体的にやりたいことができていないように感じられて、理由も明確。YouTubeに時間を吸い取られすぎている。かなりどうしようもない。Vtuberの波は自分の中でもう過ぎ去った感じがして、今はQuizKnock、チュウニズム譜面保管所、デュエプレ実況のさとらさん、GOD EATERのプレイ動画を見ている。

疲れ果ててTLを眺めていたら先生からメールが来ているのに気付いた。今日の発表内容について、関連するものが教科書の後ろのほうにあるのでちょっと読んでみてください、という感じ。該当部分を探してみると、もう知らないことだらけで全然読めずびっくりした。文章とにらめっこしているうちにPCの前で意識を飛ばしてしまったので、メールの返信もせず即座に布団にダイブして就寝。午後7時半だった。

午前1時起床。性懲りもなくYouTubeを開いたら「科学の力使いまくって隠居生活」が投稿されていたので視聴した。

www.youtube.com

布団から出て、オンサイトの参加記を必死に書いていた。朝方にとりあえず土曜日の分が書けた。まだ校閲はしていない。日記のほうはその状態で更新しておき、参加記はまた日曜日の部分が完成してから公開するつもりである。

1時間ちょっとインターン関連のコードを書き進めた後布団に入り、少しハーメルンを読んで就寝。正午を少し回ったくらいだった。

09/02(金)

午後2時半に目覚ましで起床。執念で布団から這い出た。フラフラする頭をたたき起こして午後3時から1on1。

昨日寝る前に書いていた部分を進捗として報告。月曜日の日記で言及した、別のアプローチというやつを実装した。実はこれもダメだったので、また次のアプローチを試すことになる。今度は少し実装が難しくなりそう。かなり最初のほうから候補としては上がっていたものの、とりあえず簡単に書けるものから試してみようとこれまで放置してきたのだった。今日は1時間ほどで終了。

午後4時40分から今週もサークルバチャに参加した。一応全問解いたことがあるのに、特に後ろ4問はどれも全く覚えていなかった。何とかその場で解け、1時間ちょっとで全完。4問目はエスパーした後解説を読んで感動。6問目はただ速そうだからという理由で書いたコードが実は2乗の木dpになっていたらしくびっくり。深く考えずに提出していた。7問目はしばらく有向グラフだと思い込んで苦しんだ。向きがあると解けなさそう。

https://kenkoooo.com/atcoder/?user=kotatsugame&rivals=&kind=category#/contest/show/59afc896-e37b-457a-99df-8fb92c8082aa

余った時間で解説を確認した後、1時間ほど通話を繋いで解説会に参加した。上に書いたようなことを喋っていた。

あまりの眠さにいったん仮眠をとろうと布団に横たわり、なぜか1時間ほどハーメルンを読んだ。午後8時を回っていい感じに眠気が来たので身を任せたところ、目覚ましで起きることができず、yukicoderに参加し損ねた。ハッと目を覚ましたら午後11時15分で絶望。

コンテスト終了を待ってAとBを通し、午後11時半からCF #818 div.2。

Dashboard - Codeforces Round #818 (Div. 2) - Codeforces

Aは(a/\gcd(a,b),b/\gcd(a,b))=(1,1),(1,2),(1,3),(2,1),(3,1)に限ることがわかり、それぞれ\gcd(a,b)としてあり得る数が単純な割り算で求まるので足し合わせた。Bはk\times kのマス目の((r+i)\bmod k,(c+i)\bmod k)Xを置いてコピー。

Cはまずa_i\le b_iが必要。その上でとりあえず全要素を\min bまで上げることができる。\min bを達成するインデックスはOKなので、そこから一つずつ遡って作っていくことを考え、条件を求めた。各iについてb_i=a_iまたはb_i\le b_{i+1}+1ならよい。前者を見落としていたがサンプルの最初のケースが優しかった。

Dは題意を掴むのに手間取った。2^n人が参加するトーナメント表と、各試合で左右どちらが勝ちあがるかが決まっているとする。高々k個の試合について左右の勝敗を入れ替えて優勝者の番号を最大化されるので、最初の状態をうまく決めてそれを最小化せよという問題らしい。最初の状態は1番の選手が優勝するとして、そのノードからトーナメント表、つまり二分木を根まで登る途中のどこかで勝敗が入れ替えられる。これは特にたった一か所であることがわかる。なぜなら二か所以上入れ替える場合、最も浅いところで入れ替えたノードが優勝するから。すると、高々k回の入れ替えでどこのノードが優勝できるかがわかってくる。

例えば1回戦で勝敗を入れ替える場合、選手一人が新たに優勝する可能性が出てくる。2回戦で勝敗を入れ替える場合、k\ge 2なら1回戦も入れ替えられるので選手二人が新たに優勝する可能性が出てくる。以下同様にして、i\le n回戦で勝敗を入れ替える場合、0\le j\le\min(i-1,k-1)について新たに\binom{i-1}{j}人優勝する可能性のある人が登場する。すべて足して答えればよい。iにおける和からi+1における和を求めて解いた。

日記を書いているときに、もっと見通しの良い方法を発見した。優勝者は二分木の根から勝敗に応じて左右の子に降りて行った先のノードである。常に左に降りるのを最初の状態としておいて、そこからk個まで深さを指定し、その深さでは右に降りることにした場合の行き先を考える。これは指定の方法2^n通りに対しすべて異なるから、1\dots nから高々k個選ぶ方法と優勝者は一対一対応する。つまり\sum_{i=0}^{\min(n,k)}\binom{n}{i}が答え。

Eは典型的か。g=\gcd(a,b)とし、a=ga'b=gb'と書く。cg\mid n-cを固定すると、(n-c)/ga'b'に振り分ける方法であって\gcd(a',b')=1となるようなものを数える問題になる。これは最初に1\le(n-c)/g\le nすべてに対し求めておくことができる。つまり、1\le i\le nに対し\gcd(a',b')の条件を無視するとi-1通りあって、そこからj\mid iかつj\lt iなるjについての答えを引けばよい。いわゆる除原理である。

(c,g)を探索する際は、まずgを決めて、次にn-cgの倍数で全探索すればよい。ここも除原理と同様調和級数になる。

Fはよくわからない。次数の偶奇が合っている必要があって、また辺であって結ぶ頂点の片方の値に興味がないものは、もう片方の頂点を調整するためだけに使ってよい。よってそういう辺を最初に取り除いた後、残った辺をまずいったん向き付けて、その後調整できる範囲に収まるまで有向パスを見つけてひっくり返すということを繰り返してみた。グラフの隣接リストをsetで持って実装。いかにもヤバそうだが、制約が微妙に小さいので何とか……と思いつつ投げたら爆速で通った。

全完14位。Fは両方の頂点から1引いておけば+2する頂点を選ぶ問題だと思えてフローで解ける、ということを知った。なんともしてやられた感じのする問題だった。Aは謎の式で解けるらしい。

09/05追記:n\bmod 2n\bmod 3で場合分けしているだけらしい。確かに。

yukicoderのCを解いた。DはTesterした問題なのでAC済で、EFはわからないのでまだ通していない。とりあえずここまででコメントを書いておこう。

DMを確認するとyukicoderのTester依頼が届いていて、考えているうちに微妙にやる気が出てきたので気合いを入れて起き上がった。

週記(2022/08/01-2022/08/07) - kotatsugameの日記

yukicoder contest 359 - yukicoder

Aはpopcountがちょうど2か、2以上で1が連続しているか。Bは適当にやるとダメだし、文字列を持ちながらdpできる制約でもない。少し考えて、辞書順最小の文字列とそれを達成できる位置の集合を管理すればよいことに気づいた。CはAXを混ぜて昇順にソートすればX以下の値がどこで出現するかをBITで管理できる。2本用意して個数と和を持った。

DはTesterした時にあった公式解説が大胆に削除され、僕が書き添えたTester解がトップに来ていた。つまり新たにここに書くようなことは何もない。今は余談に名残を残すのみだが、元は図表などあってかなり丁寧だったので、別に消す必要はなかったのではないかと思う。まあ今の解説に比べて複雑だったのは確かか。覚えていないのでここに再現もできない。

イナズマゼロニンという音MADが削除されたことを知った。色変記事で好きな音MADとして挙げていたのを覚えている。ひとまずタイトルがわかるように、記事に書き足しておいた。まだYouTubeに転載された動画が残っているらしい。

イナズマゼロニン https://www.nicovideo.jp/watch/sm34036224

AtCoder 赤色になりました - kotatsugameの日記

2時間ちょっと「異世界クイズ王 ~妖精世界と七王の宴~」を読み返していた。昨日の日記でも少し触れたように最近QuizKnockを見ており、そういえばクイズを題材にしたなろうがあったなと思ってチラ見して、抜け出せなくなった。

https://ncode.syosetu.com/n1867fd/

やはり面白い。ただ競技クイズの話として読んでも、面白い蘊蓄なりテクニックがいろいろ登場して唸らせられる。ただここまで気に入るのにはもっと一般的な理由があって、競技クイズに人生をかけた主人公の、常軌を逸した執念の描かれ方が非常に好み。ひたすら真剣にクイズと向き合う様が格好良くて、自分も競技プログラミングにここまで情熱を傾けられたらなあと憧れる。

朝方から昼前までは日記を書いていた。オンサイトの参加記を作っていたせいで今週分をまだ1文字も書けていなかった。とりあえず大まかな行動をChromeの閲覧履歴から復元し、火曜日の分まで文章を作ったところで切り上げて布団に移動。少しハーメルンを読んで正午就寝。

09/03(土)

午後3時過ぎに冷凍弁当を受け取った記憶がある。日記に登場するのは7月以来らしく久しぶりだが、8月の間も配達がある週は毎回注文していた。ただそのころは土曜日の午後1時から5hに参加していたので、特に睡眠を中断されることなく受け取れていたし、また弁当に言及するタイミングもなかったのだ。

またすぐ寝て、今度は午後7時半起床。ハーメルンの更新を確認したり食事して午後9時からABC267。

NEC Programming Contest 2022 (AtCoder Beginner Contest 267) - AtCoder

Aは面倒。sedで場合分けをした。先頭の大文字で三つは簡単に区別がつき、それらを取り除けば残り二つの区別も簡単。適当に目についたhを使った。Bはもっと面倒だった。言われたことを頑張って実装。

Cは差分更新が簡単に書ける。問題を開くときにDと問題名が似ていることに気づいていて、Cの内容を把握した今、Dでは連続部分列ではなくただの部分列になるんだろうなあと予想がついた。しかしCと同じ制約とするとどう解くのかわからない。O(NM)なら簡単だが……と思いつつDを開いてみると、それが通る制約になっていた。ホッとしつつ実装。

Eは二分探索を考えるとBFSで判定できる。Fは、決め打った頂点に向かってKステップ進むのをダブリングで行うことを考えていたら、最も遠い子孫に向けて進むためのダブリングテーブルを全方位木dpで管理できることに気づいた。実装はかなり頑張った。

Gを少し見て、わからなかったので順位表を開いたら、Exのほうが通されているようだったので先にそちらに進んだ。見た目と通され具合から畳み込みで何かする問題に見えて、そういうつもりで考えたらすぐ解けた。要素が奇数個という条件がなければ[x^M]\prod_i(1+x^{A_i})で書けて、\sum_i\binom{n}{2i}を二項定理から求めるのと同じ発想で[x^M]\prod_i(1-x^{A_i})を使う方法を思いついた。積をマージテクで計算して1sec強。

Gに戻る。A自体を並べ替える挿入dpの方向で考えることにした。同じ値をまとめ、小さい値から列に挿入するのを遷移にしてみる。A_i\lt A_{i+1}を満たしている位置の個数を持つと解けそうに見えたが、係数がよくわからなくなってしまったし、決めることが多すぎて間に合うように見えない。実は同じ値をまとめなくてもいいんじゃないかと思って考えると、解けた。

遷移の際はA_i\lt A_{i+1}なるiの個数、kとおく、がどのように変化するかだけに興味がある。新しい値aii+1の間に挿入する(ただし先頭への挿入をi=0で表す)とき、i=0である・A_i=aである・もともとA_i\lt A_{i+1}、のどれかが満たされている場合、kは変化せず、そうでない場合k\leftarrow k+1となる。

ここでA_i=aならばA_i\ge A_{i+1}だし、逆にA_i\lt A_{i+1}ならA_i\lt aも成り立つため、この三つの条件は実は排反である。A_i=aなるiの個数は挿入の方法に依存しないので、kだけdpで管理すれば遷移が正しく書け、これでO(NK)となり解けた。

全完11位。上に5人以上8人以下の学生がおり、賞金は得られなかった。AがFAで嬉しい。Fはどの頂点からも直径の端点が最も遠い頂点の一つになることをすっかり忘れていた。ここでの実装によるタイムロスが順位的に痛い。

コードゴルフ。Aは入力を数値に変換し、余りを取ったりしてうまく答えを作る方法。odコマンドとdcを使う方法より、Rubyで文字列のsumメソッドを使ったほうが短くなった。BはRaku。配列の添え字をJunctionにしてからBoolにキャストすることで、ORを判定するための配列アクセスを最大限に減らせる。そうして作ったBool配列を暗黙的に文字列化しつつ、正規表現/T.*F.*T/を探した。そのままだと/./が空白にマッチせず困っていたが、m/./とするとうまくいった。よくわからない。

CはRubyで書いて短くならないなあと唸っていたらPerlで大幅に縮んでいた。隙をついて現在の最短コードを取得。最初の答えを求めるのと差分更新をほぼ同じ式でできるのは、言われてみればという感じで巧妙。DはdpをAWKで書いた。木曜日に出たRTのテクニックを使っている。Eは解説のpriority_queueを使う方法をPythonで実装。以降は手付かず。

午前3時から3時間でDMOJのコンテストに出た。2600未満rated。終了が火曜日なので、感想は来週の週記に書く。

An Animal Contest 7 - DMOJ: Modern Online Judge

正午まで日記を書いていた。金曜日の終わり際まで書き上げたところで布団に入り、しばらくハーメルンを読んで午後2時前に就寝。

09/04(日)

午後8時起床。ゆっくり準備して午後9時からARC147。

AtCoder Regular Contest 147 - AtCoder

Aは操作の度にA_iが半分未満になるので、愚直にシミュレートしてよい。その値は0でなければ新しい最小値になるから、最初にソートした後はpop_back、push_frontだけで各操作を定数時間で実行できる。

Bは考察に見覚えがある。操作Bによって1個飛ばしに見た数列をソートできるので、すべての要素について最初にあったインデックスと全体をまとめてソートしたときのインデックスの偶奇が等しければよい。特に今回は順列なので、同じ値の処遇に困ることもなく、単純にP_i\bmod 2i\bmod 2を比較すればよい。ダメなインデックスは偶奇で同じ個数ずつ存在するから、それらをペアにして、操作Bで隣接させた後操作Aを1回行うことで一つずつ解消する。これが最適。

ところがWA。操作回数の見積もりに失敗していたらしい。操作Aのために一度先頭まで要素を持ってきた後、元の場所に戻すというコードを書いていたが、全要素に対してそのようなことを行う場合、操作回数は1\dots N/2の和を行き帰りで倍、偶奇で倍とおおむねN^2/2くらいになる。最後の操作Bによるソートが1\dots N/2の和を偶奇で倍にしてN^2/4であることを考えれば、最大で1.2\times 10^5回くらいの操作をすることになっていた。戻さないようにすると単純に行き帰りの倍が取れて、条件を満たせる。

Cはよくわからないまま解けた。まずr=\min Rl=\max Lを取る。r\lt lのときはその二つが違う人に対応するので、地点lrに一人ずつ並ばせて最初に戻る。l\le rのときは残る全員が[l,r]に並べるが、特にこれまでの決め方から区間内に左右に同じ人数ずついるような地点が必ず存在するため、全員をそこに並ばせるのが最適。本当にこれで解けているのか半信半疑で出したらWAで、やっぱり考察が違ったかと思いつつサンプルを試していたら、実装ミスを見つけた。r\lt lのとき二人ずつ並ばせているのを忘れており、最後、全員を一気に並ばせるときの人数が間違っていた。

Dは面白かった。スコアが積なので、各1\le x\le Mについてx\in S_jなる1\le j\le Nを選ぶ方法と読み替えておく。集合の列の前にこの選び方を固定し、列が何通りあるか考えるところまで典型。今、条件から対称差集合S_i\bigtriangleup S_{i+1}の要素はちょうど一つなので、それがxであるようなiの集合とx\in S_jを固定してみた。そのような状態が実現されるには他にどんな条件が必要か考えてみると、実は何も必要ないということに気づいた。

各集合にxが含まれるかどうかだけ考える。その状態はS_i\bigtriangleup S_{i+1}=\{x\}なるiのたびに切り替わるため、x\in S_jという状態から左右にどんどんxが含まれるかどうか決まっていく。当然矛盾が起こるはずもなく、x\in S_jを満たす列が常に、ただ一通りだけ、存在することになる。これに気付いた瞬間は本当にアハ体験だった。

つまり、jの選び方を決め打った時、どのような選び方をしてもさらに各iについてS_i\bigtriangleup S_{i+1}を決めるだけの自由度がある。それぞれN^M通りとM^{N-1}通りなので、掛け合わせて答えとなる。通ったのを確認してすぐさまdcで縮めておいた。

Eに進む。二部グラフの完全マッチングだと思えるので、Hallの結婚定理を考えてみた。点数を入れ替える人の集合をSとして、すべてのT\subseteq Sについて\#T\le\#\{i\in S\mid\exists j\in T\;s.t.\;B_i\le A_j\}が成り立つかチェックしたい。条件をよく見ると\max_{j\in T}A_jだけに依存しているから、その値をA_Tと決め打った時、Tとしては\{j\in S\mid A_j\le A_T\}を考えるのが一番\#Tが大きくなって不等式がタイトになる。つまり、これだけ見ればよい。

最初にS\{i\mid A_i\lt B_i\}とし、i\in SについてのA_iの昇順にA_T=A_iとしてチェックしていく。Bを座圧してBITのインデックスとすることで不等式の右辺が求まり、これを満たすまで新しくSに要素を追加する。どういう要素を追加するべきかというと、i\not\in SかつB_i\le A_Tはまず必要。ここでA_i\le A_Tだと\#Tも同時に増えて何も嬉しくないため、A_i\gt A_Tも必要。これらの条件を満たすiなら、以降常に不等式の右辺の集合に含まれてくれるのでB_iの詳しい値に興味はない。よってA_iが最大となるものを取って追加した。これはセグ木でできる。\#Tを求めるのに失敗して1WAしつつAC。

Fはあきらめてコードゴルフしていた。最終順位は5完で67位。DからEに進むときに少し別のことをやっていたら、Eが思ったより通されたので、順位が低くなってしまった。ペナも多い。

少し解説や最短コードをチェックした後、すぐCF。ICPCルールの5hだが強気に30分遅れのソロ参加。

Dashboard - COMPFEST 14 - Preliminary Online Mirror (Unrated, ICPC Rules, Teams Preferred) - Codeforces

ABGMHCLFKJEIをこの順で解き、12完で15位。当時のsolved順に数問開いて順に解くということを繰り返した。やはりペナルティが響いて、解いた問題数が同じ中では最下位だった。全完できなかったのは残念。

AはM=1のケースがサンプルにあって優しい。BはPが大きな人一人に小さな人を複数人くっつける。Gはb^2-a^2という形で表せる正整数のうちN番目に小さいものを求めればよい。(a,b)に対する表を作ってみると、3,5,7,\dots8,12,16,\dotsという斜めの列が見え、重複がないこと、二つの列で尽くされることは明らか。よって二分探索で解ける。

Mは右手を動かすパスを反転することで頂点1からしばらく辺を辿り、続けて逆辺を使い頂点pに辿り着くまでの時間を求めればよいとわかる。HはA_i\bmod 3A_j\bmod 3だけ見ればよいので、3\times 3通り書き出してどういう分け方をすればよいか考えた。そもそもの式の形を間違えていて1WA。

Cは直径となる2点の色が同じ場合その色はもう使えないことにだけ注意する。ある点が2本以上の直径に乗ることはないので、直径の2点をペアにして、いくつのペアが同じ色になるか全探索した。あとは組み合わせ。Lはいろいろ試した末先頭から累積和をとると隣接swapになってびっくりした。先頭の項が非負かつ広義単調増加にしたいので、最小値をチェックした後転倒数を求めればよい。最後の項を移動させられないことに気づかず1WA。

FはWを達成すること自体は+Wして-Wすれば簡単。なのであとは微調整で区間を重ねる問題になって、LSBにのみ依存することがわかる。W=2^0,2^1,\dots,2^{29}に対し、各区間\bmod Wでどの値を覆えるか求めて最も重なる位置を探すのは、イベントソートによりソート以外線形でできるから、軽めのO(N\log N)を30回行うことで前計算しておけばよい。TL 1secのところ800ms強で通った。

Kは気合い。ビルの高さに対しビルのインデックスの集合を対応させるmapを持つことは簡単に思いつく。マージテクでクエリ3には対応できそう。しかしクエリ1と2が難しい。マージテクがどちらの集合からどちらの集合に値を移すかわからないため、各インデックスがどの集合にいるかはわかっても、高さがいくらかはわからないのだ。そこで、集合と高さをペアにして持つことにした。このペアをvectorに並べ、vector上の位置を高さから求めるmap、ビルのインデックスから求める配列を持ち、丁寧に管理する。1730msで一発で通って最高。

Jは2本のパスで頂点を覆う問題になる。端点四つを適当な頂点に設定したとき、各辺についてどちらかの部分木にある端点を数え、奇数個なら+W、偶数なら+2Wを足し合わせたものがパスの重みの和になる。ただし端点を2個と2個に分けるような辺について一つだけ一回も通らない、つまり+0とすることができる。すでに置いた端点の数と+0を使ったかのフラグを持って木dp。マージが盛大に間違っていて2WA。デバッグのとき、どの頂点を根にしても答えが変わらないかチェックするのが強くて、小さいランダムケースですぐ間違いを見つけることができた。

Eは素数を全探索しても見るべき頂点数が合計O(N\log\max A)個にしかならない。dfs順で並べ、隣接する頂点のLCAを追加した木の上で答えを求めると、辺の数も同じオーダーで押さえられる。何度もグラフを作り直してdfsするのは怖かったので、dfs順に並べた後LCAが深いところを順にマージしつつ辺の寄与を数えることで解いた。マージをミスして1WA。

Iは問題文がいかついが解法は簡単。2番目と4番目の条件から、key treeの辺を順に見て二つの連結成分をマージするとき、同じタイミングでそれぞれの連結成分から1点ずつ取ったものに共通する閉路を作る必要がある。そのために辺を2本使うので確かに全体で2N-2本となっている。辺は区別できるので、繋ぎ方は連結成分のサイズをxyとしたとき(xy)^2。次に辺の重みについて、長さ2N-2の順列PであってP_{2i-1}\lt P_{2i}かつP_{2i}\lt P_{2i+2}なるものが対応する。P_{2i-1}P_{2i}が一度に使う辺の重みである。一つ目の不等式は先に2本の辺を区別したからで、二つ目の不等式は4番目の条件のため。これは実験すると(2N-3)!!通りあった。掛け合わせて答えになる。

Dに50分残したものの解けなかった。1,2,3,3,4,4,\dotsという数列になると思ったのだが、わざわざTLが2secになっていることだしこんな簡単ではないのだろう。そうやって最後1時間は不完全燃焼気味。しかし逆に、それ以外の3時間30分はコンスタントにAC数を伸ばせて非常に気持ちよかった。このくらいの難易度の5hならソロ参加がかなり楽しいと感じている。

ARCのコードゴルフ。AはPerlで、for文でループしているリストを中で伸ばすと非常にシンプルに書ける。ただしmapを使ったり、リストへの代入をループと一緒に書くと、伸ばした分までループが回らないのでうまくいかない。Dはコンテスト中のdcが提出時間の差で勝っていた。残りC以外は手付かず。

CはOctave。答えがdouble型だと精度が足りないくらい大きくなるせいで、せっかく二つのベクトルの内積で書けるのに行列積を使えない。要素ごとに積をとってsum関数に投げてもダメだったが、これはsumがデフォルトでは内部でdouble型を使うからで、第2引数に"native"を指定することで元のデータ型を保ってくれるらしい。引数をint64にキャストしてそれを使ったら通った。元のコードからはかなり長くなってしまったが、別の言語で書くよりはまだ短そう。Octaveは大きなケースでのWAに苦しめられがちなので、他の問題でも使いどころがあったはず……と思いつつ、見つけられてはいない。WAだったコードを調べる必要があって難しい。

朝からはまた日記を書いていた。日曜日のARC途中まで書いて切り上げ、布団に移動。少しハーメルンを読んで午後1時就寝。結局今週中に参加記を完成させられなかった。情けないぜ。

週記(2022/08/22-2022/08/28)

工事中です。

08/22(月)

午後4時過ぎ起床。Multi-Universities Campが終了したため思う存分眠れるというわけではなく、もともと月曜日はインターン先定例会でこのくらいの時間には起きなければならなかった。

メールが2通届いていた。1通目はABC264の賞金について。総合順位3位と学生順位2位のダブル受賞で合わせて45000円のアマゾンギフト券が貰えるらしい。もちろん嬉しさはあるが、本当に1WAで25000円減っていたので少しショックを受けた。

Eの1WAが響いて2位から3位に転落し、賞金額も25000円くらい減ったように見える。

週記(2022/08/08-2022/08/14) - kotatsugameの日記

2通目は日本最強プログラマー学生選手権本選について。無事現地で開催されることになったようだ。一泊するのも含めてかなり楽しみである。

午後4時半から定例会。先週の進捗はなし。実は今振られているタスクの重要度が思ったより高そうで、結構まずいように見える。今週こそは頑張りたい。勉強会はKaggleコンペの参加記で、非常に面白かった。もちろん2か月以上コンペに参加されていたのだから、今日の発表の裏にはそれに何倍もする試行錯誤があったのだろうが、正解のルートだけ再現してもらって眺めるのは手軽で分かりやすい。ただ自分でやるのは……。一時期触っていたものの、結局すぐにフェードアウトしてしまった。やっぱり今は競プロのほうに力を入れたい。

終わってからはずっと先週の週記を書いていた。まず文章チェックをした後、5hについて三つ書く。ただそれだけのはずだったのにやたら時間がかかり、微妙に未完成のままいったん投稿する羽目になった。

午後11時半からはCodechef、August Lunchtime 2022 div.1。UIがいろいろ変わっていて少し戸惑った。特にコンテストページにLive Ratings Graphというpredictorの高機能版みたいなものが表示されていたのが目に付いた。気が散るので今のところはとりあえず非表示にしておきたい。

https://www.codechef.com/LTIME111A

TREECLRはよくわからない。木dpをしようとしてもうまくいかなかったので、dfs順に適当に決めていくことにした。結局距離2以内にある頂点と色が異なればよいため、すでに色を塗った頂点であって距離が近いものを数えながらdfsした。飛び飛びに塗ると壊れるが、わざわざそのようなことはしないのでOK。ちなみにこれはABCで既出だったらしい。

E - Virus Tree 2

SUB12OPは謎。A_{i+1}\ge 2であればA_iが何であっても操作するだけ得になる。よってまずは後ろから順に貪欲に操作してみた。これを終えるとすべてのiA_i\le 1となるが、特にA_i=A_{i+1}=1の場合のみ、ここで操作すると得する。これも貪欲に対処して、結果を答えとした。恐る恐る投げたら通った。

SORTARRAYは簡単。操作する区間が重なることはないので、先頭から順にその位置までソートできた段階での操作回数の最小を持てばよい。A_i\le A_{i+1}のときi\rightarrow i+1という操作回数を変えない遷移があり、またi\lt jかつA_i=A_jなるjについてi\rightarrow jという操作回数を1増やす遷移がある。後者はA_iごとに値を持っておく。

MEXPERMDIFは大変だった。P全体のMEXは必ずN+1になるのでA-Bでは打ち消しあう。ここを無視すると、なんとB=\sum_{i=0}^N\min(P_0,\dots,P_i)と書けた。APを反転すると同様。Bが最大となるのは明らかにP=N,N-1,\dots,0の時であり、このときA=0よりK=N(N+1)/2。これがKとして達成できる最大になりそう。

ここから1ずつ減らしていくことを考えていたら、P_N=0のときPの転倒数を減らしていくような感じでB=K1ずつデクリメントしていけることに気づき、N\le K\le N(N+1)/2の構築ができた。ここからAも非ゼロにしてみようと考え、試しにP_{N-1}=0P_N=Nとしたところ、同様の議論でN-1-N\le B-A\le N(N-1)/2-Nが得られた。よってK\le N(N-1)/2-Nの場合の構築もできた。

この二つでカバーできず、かつK\le N(N+1)/2となるような(N,K)がどれくらいあるのか求めてみる。そもそもN(N-1)/2-N\lt Nを式変形するとN\lt 5となるし、実際全探索してみるとたったの6通りしかなかった。これらは埋め込んでAC。

MEXSUBTRは通せなかった。まずB_{P_i}\ge B_iが必要。また各頂点に割り振る値の条件として、その頂点自身または祖先のBであってはいけないという条件がある。この元で、各頂点uについて子のBの最大値B'_u、ただし子が存在しなければ0とする、を求め、u以下の部分木にB'_u\dots B_u-1がすべて出現するようにできればOKである。

根からdfsして必要な値を管理し、葉から順にそこに割り振れる最小の値をどんどん入れていくコードをまず書いた。WA。B=0なる頂点では割り振る値がある程度大きい必要があるため、大きな値はできるだけそういう頂点に割り振るべきである。よって今度は逆に必要な値を大きいほうから見てどんどん割り振っていくコードを書いた。これもWA。

デバッグを進めるうちに、2種類のコードで落ちるケースがdisjointになったことに気づいた。しかしマージしてみたところ、WAのケースが二つの和集合になってしまいどうにもならない。結局修正できず終わった。4完15位でレートは2773→2799(+26)、highest。

MEXSUBTRについて、どうやら後者のコードで不可能かの判定が間違っていたらしい。先に葉のほうに大きな値を割り振った結果、部分木内で必要な値を入れる頂点が確保できなくなってしまうケースがある。つまり、二つのコードをマージするときは答えの\min\maxを取るのではなく、判定が食い違ったときに割り振りが成功したほうの答えを出力するようにすべきだったらしい。別に正当でも何でもないが、先ほども述べたように落ちるケースがdisjointなので、これで通った。

その後ちゃんとupsolveした。下の部分木から順に決めていくのと、割り振れる値の最小値が大きい頂点から使うのを両方正しく実装すればよい。multisetで管理してギリギリの頂点を使うようにし、まだ使っていない頂点集合をマージテクで取り回したら通った。

先週の週記を改めて完成させた後、今日の日記を書いていた。午前7時半ごろ終えて布団に入り、眠気の限界までハーメルンを読んで午前10時就寝。

08/23(火)

午後6時起床。外出しようかななどと考えていたものの、もはやそのような時間ではなかった。意気消沈し、しばらくハーメルンを読んで午後8時半頃に寝落ちした。

08/24(水)

午前0時起床。ずっとハーメルンを読み続けていたが、さすがに積読が気になる。少し主人公が苦しむ展開となったあたりで離脱し、今度はラノベを読み始めた。昼間までに2冊読了。

1冊目、「ひきこもりの俺がかわいいギルドマスターに世話を焼かれまくったって別にいいだろう?」2巻。感想が何も出てこない。ストーリー的な面白さは皆無だったが、キャラがコミカルでかわいいし苦しい展開が存在しないため、読んでいて全く苦にはならなかった。そういうのもラノベとしての面白さの一種だろうか。コミカライズが開始するくらい売れているというのは自分の感覚とは全く合わないが、好みとはもともとそういうものである。

2冊目、「ログアウトしたのはVRMMOじゃなく本物の異世界でした」2巻。主人公が異世界でもう一度VRMMOを始めたため、ゲーム内と現実世界でイベントが交互に進行して行った。今のところゲーム内で何か活躍したわけではないためそちらは微妙。一方現実世界のほうはこの巻でも無双しっぱなしでよい。ただし、周囲の人が主人公に教えを乞い、また主人公のほうもそれに応えて指導するような立ち位置に回りがちだったのが読んでいて少し不満だった。他の人との関係にどれも上下の差があるように見えてどことなく不安な気持ちになるし、教えてばかりでは見せ場を失っているようで残念。活動場所が主人公のレベルに合っていないのもモヤモヤした。

その間にやっていたことを二つばかり。まず、TCO Finalsの旅程が提案された。見ると山形空港から朝9時に出発することになっていてひっくり返ってしまった。場所も時間も伝えた要望と全く異なっている。慌てて、そういう理由だからこのスケジュールでは全く受け入れられないということを伝え、旅行代理店にもう一度確認してもらった。

次に、TLにURLが流れてきたため、午前7時半からあさかつというバーチャルコンテストに出た。20分弱で全完。たまに出てるよなと思って日記を検索したら、前回出たのが1年以上前だった。

https://kenkoooo.com/atcoder/#/contest/show/725415d4-0843-48f6-97cb-2ed7026be7f9

あさかつの並走者を募集するツイートを見かけたので、yukicoderを途中で切り上げて参加してみた。

週記(2021/06/28-2021/07/04) - kotatsugameの日記

正午くらいに学食に向かって昼食。今日も納豆2個をミールカードで会計する件で店員に止められたので、以下のような決まりになっていると指摘した。すると別の店員に確認しに行ったのだが、そこにたまたま当時担当した人がいたため説明が伝わって、無事同様の対応をしてもらうことに成功した。

レジを2周せずとも一旦お盆から出すなりすればその場で2回会計を行ってもらえることになった。今回以降ずっとこれで良いという確認は取っている。

週記(2022/06/27-2022/07/03) - kotatsugameの日記

しかし確認を待つ間に後ろに行列ができてストレスになったし、会計を終えてその場を離れたときに後ろから「言ってくれないとわかんないよ~」など聞こえてきて大変腹が立った。最初は聞こえよがしに言っているのかと思って腹を立てていたが、これはただの被害妄想である可能性が高い。そもそも、このような対応の存在を店員に周知していない学食側に非があるため、その辺りを理由に腹を立てるべきだった。もしかして1か月近く間が空いたから僕がもう来ないと思ったのか?絶対卒業するまで定期的にバトルしてやるからな。

散髪し、購買で予約していたラノベを購入した後、バイク屋に向かった。原付の保険更新。しかし今年は自賠責保険と任意保険が同時に切れるようで、任意保険分の金額しか用意してこなかったので、改めて耳を揃え近くまた来なければならない。

午後2時過ぎに帰宅。しばらくYouTubeを見てから週末のオンサイト用の切符を購入するために家を出た。出がけにAtCoder Problemsを確認したら、最短コード保持数がついに2000の大台に乗っていた。

みどりの窓口に並んで無事購入。今回は学割ではなく週末パスを利用することにした。一応領収書も貰っておいたものの、必要ないはず。思ったより安く切符を購入できたため伝えていた交通費と差が出てしまったが、この差額はそのまま僕の懐に入ることになる。以前も学割の割引分がそうなったのを覚えている。本当にいいのだろうか。

午後4時から4時間ほどゲーセンで遊んだ。イヤホンを変えたら音の聞こえ方がだいぶ変わって、LATEが多く出るようになったため少し判定をずらした。今日は新曲を埋めた後、スマホアームを設置して手元を撮っていた。スコア的な成果は14のAJ+1、14+のSSS+が+1。

撮った手元は以下のリプライツリーにまとめている。Ascension to Heavenは今日3回目で、なぜか上手かった。後半は1回目のほうが上手かったのだが、譜面を少し忘れていて途中の5鍵に対応し損ねたためスコアは低くなってしまった。Vallistaも今日3回目。以前のAJがまぐれで出たものではないということが確認できて嬉しい。

きゅうりバーにダイブはお題を募集したときにリプライされたもの。中盤の1分の1ノーツ3連打を片手トリルで取る運指はお気に入り。しかし今日は全然うまくいかず、AJまで20回以上プレイする羽目になった。変なところでアタックがたくさん出るのに悩まされていたが、後から、指一本でもスライダーの上下で判定が分かれて巻き込むため、上か下の半分だけ押さえるようにするべきとの指摘を受けた。

ところで、後ろに並んでいるmaimaiのボタンの音がかなり大きく入り込んでいてびっくりした。実際にプレイしている身としてはイヤホンをつけているためかそこまでうるさい印象はない。

油そばを食べ、帰宅。手元動画をツイートした。眠気の限界で動画を確認しつつ何度も意識を飛ばしそうになっていた。リプライツリーにまとめ一気に送信し、シャワーを浴びて戻ってきたらすでにツイートが完了していた。改めてチェックしてすぐ就寝。午後10時半だった。

08/25(木)

午前7時起床。正午くらいまで布団に横たわったままグラフ理論の教科書を読んでいた。

布団から出たら昨日のTCO Finalsの旅程についてメールが来ていた。仙台空港から出発したいと言っていたのだが、そこから東京や大阪に飛ぶ国内線がなく、フライトを組み立てられなかったらしい。仕方ないので羽田空港を指定して、そこまでの行き帰りは自費で賄うことにした。もともと自宅と空港の間の交通費は出してくれないのだ。まあそれくらいならいいかという気持ち。

シャワーを浴びて午後1時過ぎに大学に向け出発。途中購買でお菓子類を買い込みつつ、以前伝えられていた時刻である午後1時半に普段セミナーで使っていた教室に着いた。しかし誰もいない。なぜかZoomリンクがメールされていたので、今日はオンラインになったのかな?と思い接続。チャットでどこにいるか聞いてみたら、一つ下の階の教室だった。急いで向かい、無事合流した後は、今日は指導教員の先生と博士課程の人の発表を聞くだけで終わった。

終わってからしばらく、なぜか同じ教室にいたB2の人たちを交えて話していた。今日は通常のセミナーではなく、指導教員の先生が前期に行った講義の一部みたいな扱いだったらしい。なのでB2の人もいたというわけ。では時間と場所はいつ知らされたのか、といつもセミナーをしているもう一人に聞いてみたら、1週間前にメールが来たとのこと。自分のメールボックスを調べると、あった。しかも「承知しました」と返信すらしている。なぜこのようなやらかしをしてしまったのか……。

午後6時に帰宅。しばらくYouTubeを見ていたら、先生からR言語に関する頼み事のメールが来て、それで日付が変わるくらいまではずっと調べ物をしていた。

ハーメルンを読み返したり、インターン関連のコードを書いたりして午前4時。ラノベを読んで午前5時半に寝た。

08/26(金)

正午起床。布団に転がったままYouTubeを眺めて1時間くらいドブに捨てた。起きて外出。

まず学食。今日も納豆を2パック購入したが、特に何も言われず一度に2パック会計してもらえた。なにも指摘せずに利用している僕も僕であるとはいえ、このように店員によって対応に差があるのもモヤモヤポイント。最近YouTubeで納豆のシートをパックの口を閉めて引き抜くように外しているのを見たので、試してみたところ、シートについた納豆をチマチマ外す手間がなくなり非常に簡単でよかった。

この後の行動は水曜日とほぼ同じだった。ラノベを受け取り、バイク屋に向かって今度は自賠責保険の更新をして帰宅。

少しコードを書き進め、午後3時から1on1。昨日今日で産んだ進捗を報告した。自分でそれなりに形になってきたと思っていたのに、かなりまずいケースが見つかって、その場でなんとか修正しようとするうち至る所が崩壊。試した方法ではうまくいかないというのが分かったので、別の方法が提案され、それを実装して動作させるのが来週までの課題となった。今日は1時間で終了。

しばらくYouTubeに意識を吸い取られていたが、サークルのバーチャルコンテストがもうすぐの午後4時40分から始まるということを知り、参加を決めた。

https://kenkoooo.com/atcoder/?user=kotatsugame&rivals=&kind=category#/contest/show/4dd18aab-6fc0-4698-938e-2127ea28d7a0

全問解いたことがあるのに詳細を忘れているものが多く、特に5問目は考察に苦労。7問目は逆につい最近の問題で考察をほとんど覚えていたが、これは実装が辛いタイプの問題で、今日も苦しんで通した。何とか1時間で全完。

残り時間で各問題の解説を確認し、さらに1問目の最短コードを縮めることに成功した。元のコードはdcで、特に言語アップデートが全体に入る前、テクニックがまだ熟成されていないころのもの。もしかしたらこういう見落としがまだいくつも残っているのかもしれない。

atcoder.jp

バーチャルコンテスト終了後、1時間ほど通話をつないで解説会が行われた。いろいろ話した後、参加者が全員日曜日のオンサイトに参加する人だったため、当日はよろしくお願いしますと言いつつ終了した。

しばらく日記を書いて午後9時20分からyukicoder。

yukicoder contest 358 - yukicoder

Aはよい。Bは-1の個数をN/2以下で決め打って、1と交互に並べたときにf(S)がどのような値になるか紙で計算。個数がちょうどN/2のときとそうでないときで微妙に違う式が出たので、それぞれで最小値を求め、\minを取った。

Cは「良い数列」の先頭から累積XORを取ると末尾が001列が得られ、これの逆変換を考えることで末尾が001列と「良い数列」が一対一対応することがわかる。変換後に1になった箇所で順に操作するのが最適だから、f(S)の値が出現する1の個数と一致する。よってこれを固定し、変換後の列として数え上げればよい。

DはN\le 3で実験し、N=4を考えているうちに、偶数番目の山だけ見てNimをすればよいのではないかと感づいた。山1以外に移す操作に対してはほぼキャンセルのような操作ができるし、奇数番目の山からは山1に移せないため。出したら通った。

Eは上のbitからどこかで立てるか立てないかを決めていく。Fも上のbitから見て、大小関係がそのbitまでで定まらないグループに分けていく。

Gは\bmod{998244353\times 999630629}を考えてみたりするもうまくいかない。しばらくして、\sum_{i\in S}A_iがそれほど大きくならないことに気づき、これが999630629以上となるようなSの数が数えられれば十分であることが分かった。逆にS^c=\{1,2,\dots,N\}\setminus Sを考えてもよくて、\sum_{i\in S^c}A_i\le \sum A-999630629\lt 4\times 10^5が条件となる。単に部分和dpでは間に合いそうにないが、A_iの値が同じiを数えて一気に遷移するようにしたら通った。

Gの解説を読むととんでもないことが書いてあってびっくり。これが想定解で星3.5というのはさすがにヤバいだろう。ただ、そもそも\sum A\ge 999630629という条件が強いので、自分のような解法を落とせるケースがあるとも思えない。詳しい解析をしようと思ったもののAの分布を調べる必要があって断念。TLで、その条件が成り立つときAの種類数が\sqrt{4\times 10^5}以下であるという事実が流れてきたが、解析に使えるかはわからない。

それから深夜までまた日記を書いていた。そろそろ寝ようかと思った頃合いでMeta Hacker Cupの今年の予選が始まったので、参加。B2を提出したときにエラーが出てしまったが、それ以外は問題なく進んで、順位表からわかるように1時間ちょっとでD問題の提出を終えた。コンテスト終了が月曜深夜なので、内容に対する感想は来週の週記に書こう。

Meta Hacker Cup - 2022 - Qualification Round

明日の準備としてノートPCのUbuntuのアップデートをしていたら、バージョンを上げられるらしかったので、上げておいた。Dockに表示したくないアイコンが生えていたので、いろいろ調べてgsettingsを弄り、消すことに成功した。

荷造りは明日することにして何が必要かだけリストアップし、午前4時半就寝。

08/27(土)

午前8時半ごろに目が覚めてしまい、二度寝できなかった。1時間ほどスマホを触った後布団から脱出。

昨日のMHCのB2について、エラーが出た後ソースコードと、出力ファイルをzip圧縮したものをclarに送り付けておいたのだが、今見ると提出が行われたことになっていた。送ったファイルから作ったとのこと。これで無事全問に対し提出できたことになって、今のところの順位は17位。これならちゃんと午前2時を待って真面目に参加すればよかった。そもそも昨日はそのくらいの時間には寝ている予定だったのだ。

ここに参加記が入る

地下鉄に乗るため歩いているとき、国際センター駅の前の道路が渋滞していてびっくりした。駐車場に入るための車列だったようだ。何が行われるのだろうか。

ホテルのチェックインについて。プール利用の説明が始まってびっくりした。ちゃんと安いプランを選んだはずなので、このために宿泊費が高くなっているということはないはず。水着を用意していないため適当に聞き流した。また、宿泊費を現金で支払ったらお釣りを1800円多く渡されてびっくりした。あまり頭が回っておらず一度財布に入れてから気付いたため、向こうから渡されたピン札に癖を付けて返してしまいちょっと申し訳ない気持ちがある。そう、ちゃんと全部ピン札でお釣りが来たのだ。やはり丁寧だなあとの感想を抱いた。

昼食について。まだ開いている店でちゃんと食事できるところもあったが、中国料理でランチのコースですら6000円弱とさすがに厳しい。そもそも中国料理にあまり興味がない。しかしディナーがその倍以上することを知った今は、せっかくなら挑戦してみてもよかったかもしれないと思う。いや、その場合は夕食をあまり食べられなかっただろうし、難しいところ。

ちなみに選んだ店は、最初喫茶メニューだけということを知らずに入って、やっぱりいいですと出て行った経緯がある。それでもう一回来たのだから店員さんに覚えられていて、上の中国料理の店なら今も食べられますよと言われたのだった。今たくさん食べると夜つらいなどモゴモゴ言ってその場は流したが、そこを選択しなかった一番大きな理由は金銭的な理由である。そんなことはさすがに言えない。

参加記のほうでは写真を貼ったので、日記ではツイートを貼っておこう。ちなみに1800円だった。これに関しては喫茶店なんてほとんど入ったことがないので、そんなものか、くらいの感覚。

ゲーセンについて。GiGO新宿西口店に行った。ここは地下1階に音ゲーコーナーがあり店の外のエレベーターや階段を使って降りる必要があるのになかなか気づけず、しばらくUFOキャッチャーの間を彷徨い歩いていた。手袋を持ってきていないので素手でのプレイになったが、まあまあ上手かった。13+の1落ちは驚き。ただのラッキーに思えるので理論値を狙う気はない。「ぼくらの16bit戦争」99AJは今日一発。上手すぎ。終盤の速いトリルで結構出したのが残念だった。

夕食について。ゲーセンでお会いした面々との夕食は感染症対策と言って遠慮したのだが、実際は一人で豪遊する予定だったからである。ところが予想以上にディナーが高かった。金銭自体はATMに走れば何とでもなるものの、そもそも服装が見合っていない。Tシャツの上に一枚羽織ってみたのもどのくらい効果があるだろうか、1万円を超えるような店にはさすがに入れない。御膳は5800円だったのでこれなら許されるだろう。

店に入ると昼と同じ店員さんに案内された。まだ僕のことを覚えていたらしく、特にTシャツの柄であった立山ライチョウについて言及された。この店では立山という富山の日本酒を置いているらしいが、コンテストもあるし財布も心もとないので今日は遠慮した。ところでTシャツを覚えられているのは実は良くなかったのではないか?昼もちゃんと上着を着ていけばよかった。背伸びしているなあくらいの寛大さで許してほしい。

最初に一品出てきたので御膳と言いつつ懐石料理っぽく出てくるのかと思ったら、全然そんなことはなかった。ただのお通しだったらしい。普段の食事と同じ感じでガツガツ食べ進めたら15分で完食してしまった。味わおうと努力はしていたもののほとんど何もわからなかった。どれも美味しいのは確かであるが、どう美味しいのかを自分の経験と比較できず、表現できない。普段適当なものしか食べていないのに都合よく舌が鋭敏になったりはしないということ。

ただ、こういう値段がつけられた食事をするという体験ができただけで十分よかった。できるならやっておくべきことの一つだと思う。今必要かはともかく。

シャワーを浴びた後少し時間が空いたので、その間は今日の行動記録を書いていた。上のエピソードは大体この時に記録しておいたものから復元している。そういえば、ホテルのエレベーターが高層階と低層階で分かれていたのも面白かったが、言及するタイミングが見当たらなかったのでここに書いておこう。

午後9時からABC266に出た。

AtCoder Beginner Contest 266 - AtCoder

Aは咄嗟に短い書き方が思いつけなかったのでC++で書いた。BはまずRubyで書いて、その後言語を変えつついろいろ試してみた。負の数のせいでdcやAWKが弱く、PerlとRakuの17Bが作れた中では最短。Cは外積で角度を判定。DとEはdp。Eはどちらかといえば漸化式が近いか。Fはなもりグラフの閉路を検出するだけで、いつかライブラリにしないとなと思いつつ今日もフルスクラッチした。

GはRGの個数で包除原理。RGを組にしておいてRGBとともに並べる場合の数を数えるのは、前計算により定数時間で行える。RGK個作った場合からK+1個作った場合を引くだけで答えが出ると思い込み、実際にサンプルもあったので提出。しかしWA。サンプルをよく見ると、そもそもK+1個作れないケースしかなくてびっくりした。改めてK以上\min(R,G)以下を全探索し、係数を正しく掛けてようやくAC。

Exは解けず。あるすぬけ君を捕まえた時刻・座標からスタートして別のすぬけ君を捕まえられる条件を書き出してみると、TYXT-Y-XまたはT-Y+Xそれぞれについての大小関係四つの条件で表せることが分かった。うち一つはソートで対応できる。残り三つを3次元セグ木で表現しようとして、残り時間をすべてつぎ込んでフルスクラッチ。50分くらいかけて完成したが当然のようにTLEし、手元で16secくらいかかったままどうしようもできず、コンテスト終了。

7完33位。Exはもうちょっと頭を使うとTに関する条件が削れて、2次元セグ木で解けるようになるらしい。あとはセグメント木ではなくBITを使うと速いとも指摘されたが、改めて書き直すのが面倒で、3次元BITが通るのかはまだ試していない。

しばらくコードゴルフしていた。以降の内容はECR後に少し縮めたものも含む。Aは最初と最後の文字を削除するのを繰り返すというVimの解が上手い。Bはやはりどうしようもなかったようで、コンテスト中の17Bがそのまま最短になっていた。CはACBDが交差するか判定する解法が上手いなと思ったものの、コードとしては普通に偏角を見るほうが短いようだ。Eは答えに6^Nを掛けたものをOEISで探したがなく、ループを回して計算。直前の値を整数に切り捨てたものを見れば1から6までのうちどの出目まで振りなおすかがわかるため、漸化式が閉じた形で書ける。他は手付かず。

午後11時半からECR134に出た。

Dashboard - Educational Codeforces Round 134 (Rated for Div. 2) - Codeforces

Aは何かうまい方法があるのかよくわからない。色の出現回数の列としてあり得るものを全列挙し、それぞれ手で答えを求めた。Bはx=1y=mを通るか、y=1x=nを通るのが最適。それぞれ(s_x,s_y)に最も近づくマスだけ見て判定した。

Cは、a_i\le b_jとなる最小のjについてd_i^{\min}=b_j-a_iとなる。このjは制約よりj\le iを満たすが、特にj=iとなるiについて、その位置より前にあるi'がその位置以降にあるj'に対応することはない。逆にそうでなければ対応させられるということからd^{\max}がわかる。

Dは上の桁から見て0110をペアにする。要素数が食い違ってうまくペアにできない場合はそのbitを立てるのを諦め、ペアにできた場合は列を分割してそれぞれ計算。ペアにできないと分かった桁でうっかり分割してしまわないよう気を付ける。vectorを作ったり消したりするので、代入をいくつかswapで書き換えておこうとしたら、ミスして1WA。

Eは面倒。s+tにおけるprefix functionの最後の要素p_{|s|+|t|}kとする。k\le|s|かつ|t|\lt kの場合、sの末尾k-|t|文字がsの先頭k-|t|文字と一致する。これはZ-algorithmで検出することができて、さらにsk-|t|+1文字目から|t|文字に紐づけて答えを保存しておくことで、tからこの値を検索することも可能。Trie木で持つことで、tを1文字ずつ伸ばしながら検索するのは非常に高速になる。

k\le|t|の場合はkが小さいのである程度愚直でもOK。あまりに愚直に実装するとO(|t|^2)かかり、tを1文字伸ばすことも考えると3乗になってしまうが、tを1文字伸ばした時このkが高々1しか増えないことに注目すれば、全体でO(|t|^2)に改善することができる。

|s|,|t|\lt kの場合が問題で、このケースにはサンプルを試して気づいた。ただ対処は簡単、直前の答えを1増やせるか毎回判定するだけでよい。なぜならこのケースはtを縮めていくことでそのうち|s|=kのケースに対応することになって、上のk\le |s|で検出できるから。見落としているケースがないか心配で恐る恐る出したら、通った。

Fは謎。わからなすぎて寝ようとしたが、ふと二部マッチングの構造を調べるのにDM分解という方法があったなと思いだし、検索した。以下のページや検索して出てきたPDFを見つつ考えるうちに、最大マッチングのサイズを減らすには頂点を一つずつ削除するだけで良いのではないかと気づいた。ページの用語を流用すれば、W_0^+の頂点と、W_i^-(ただし1\le i\le K+1)の頂点を順番に削除する。辺の管理も、最初の状態における最大マッチングから1本ずつ減っていくだけなので簡単。試しに出してみたら通った。

Dulmage–Mendelsohn decomposition (DM 分解) | cplib-cpp

全完4位。Fの形式は面白かった。最大マッチングを常に管理させておきたいが、毎回出力させるのは不可能。そこで、いつもは辺番号の和だけ出力させる。そこから最大マッチングを復元するのは不可能なので、ジャッジはこれを無視しているはず。ただし、たまにオンラインクエリでそれを達成する最大マッチング自体を聞かれるので、解答プログラムは常に正しい最大マッチングを管理しておかなければならない。

1時間半で全完できたのでコンテスト終了を待たず寝ようとしていたのに、準備を整えている間に終わってしまった。少しTLの感想戦を眺めて、就寝したのが午前2時前だった。

08/28(日)

午前8時起床。

朝食について。朝食会場は僕らが行った店のほかにもう一つあった。そちらはビュッフェ形式もやっていて、それで朝食の量を気にしていたのだ。ビュッフェで大量に食べるのとお替り自由のご飯を大量に食べるのは違うだろうが。一方その店は僕らがレストランフロアに降りてきたとき非常に混んでいた。僕らが入った店では並ばず食べられた、というのには満足。ちなみにどちらでも普通に食べると3500円らしい。朝食付きプランにして大正解。

週記(2022/08/15-2022/08/21)

08/15(月)

正午起床。もう30分くらい眠るつもりだったがTLを見ているうちに残念ながらそのような余裕はなくなってしまった。食事し、午後1時からMulti-Universities Camp 9回目。

"蔚来杯"2022牛客暑期多校训练营9_ACM/NOIP/NOI/CCPC/ICPC算法编程基础集训_牛客竞赛OJ

チームでAKEBGFCIDの9完、3位。自分はEFCを解いた。

Eはちょっとした構築。次にGがかなり解かれていたので読み、わからなかったのでHELPを投げた。少し相談して文字列の回文列挙をする問題になったが、ローリングハッシュが必要そうで、フルスクラッチする自信がなかったので実装をチームメイトに押し付けた。

Fはn\times m行列が与えられ、連続な部分行列について要素の\gcdを考えたとき、その総和を求める問題。ただし元の行列の要素には1\dots nmが一つずつ現れる。O(n^2 m)がギリギリ通りそうな制約に見えたが、よく考えると\gcdを求めるのに\logがついて厳しそう。より高速な方法を考えた。1\le g\le nmについて、gで割り切れるような要素のみからなる部分行列を数え上げられればよい。このとき見るべきマスの総数が調和級数O(nm\log nm)になる。

見るべきマスを左上から順にソートして、各マスについてそこを右下とするような部分行列を数える。まず左に連続するgで割り切れる要素の数が求まり、これをL_{i,j}とすると、求める部分行列の数は\sum_{k\le i}\min_{k\le i'\le i}L_{i',j}となる。すると列ごとにstackで管理するような形で差分更新ができて解ける。ソート以外は見るべきマスの総数に対して線形になって、\gcdも使わないのでO(n^2 m)よりは高速。出したらWAで何事かと思ったが、なんとnmを取り違えていた部分があった。

Cは重み付きの単純グラフに関する問題。ただしこの重みは、辺eについて順方向に通れば+P_e、逆方向に通れば-P_eとなる。このPは3次元だが面倒なだけであまり関係ない。すべてのループについて重みの和がちょうど\vec 0になるはずという状況下で、P_eが一つだけ間違っている場合、間違っているものとしてあり得るeを列挙せよというもの。

サイクル基底を考えると、dfs木を取って後退辺を1本だけ含むようなループをチェックすればよい。最初から矛盾がないなら、逆にループに含まれる重みを変更すると矛盾してしまうので、ループに含まれない辺が答えとなる。矛盾しているループがあるならそれらのループにすべて共通する辺が答え。どちらも適切な後退辺を選び、端点に\pm 1した後木上のimos法を行うことで判定できる。矛盾しているループが一つのみなら、それを作った後退辺も答えに含めることに注意。

Dはn\times mの盤面を、ちょうどnm/2回曲がるような縦横のみの折れ線で一筆書きせよという構築問題。ただしn,mは偶数。片方が4の倍数である場合の構築を自分が考え、それをチームメイトが拡張して両方4の倍数でない場合にも対応させた。このあたりで自分が抜ける時間になったので実装を託したが、その後気合いで200行くらい実装して無事通ったらしい。

午後4時半からインターン先定例会。先週は結局帰省しなかったものの、稼働してもいない。よって報告すべき進捗がない。紙幅を埋めるため、または単純に成果を誇るという気持ちで、AtCoderで冠が付いたという話をした。少し反応があって満足。勉強会は……よくわからない。今週の内容もまたあまり興味を持てなかった。感想すら言えなかったのは集中していなかったということで、興味を持てないからと他のことに意識を向けているのはダメだろう。気を付けたい。

先週の週記は土曜日のCF以降と校閲を残すのみで、ここ最近では最も余裕のある月曜日。TLを見たりYouTubeを見たりと盛大に集中を切らしながら、何とか日付が変わる前に読み返しまで済ませた状態の週記を投稿できた。

腹が減ったのでコンビニに行く。部屋の外に出るのは先週火曜日以来らしい。最寄りの店舗は午前0時で閉まっていたので、原付を飛ばして普段行かない方面に向かい、無事開いているコンビニを見つけて食欲の赴くままにパンやおにぎりを買ってきた。

深夜なのでほとんど車がいないとはいえ、それを補って余りあるほど、あまり知らない道というのは非常に怖い。本当はゆっくり周囲を確認しながら走りたいのに、原付のスピードではままならないのだ。今日使ったコンビニは交差点のすぐそばにあって、発見したときはスピードを落としていた状態だったし、大きな駐車場が併設されていたため入りやすくて助かった。

少しTCO Finalsの書類作成を進め、疑問点をメールした後、朝までAHC013に取り組んでいた。この日記が公開される頃にはコンテストは終了しているから、少しばかり書き記しておこう。

RECRUIT Nihonbashi Half Marathon 2022 Summer(AHC013) - AtCoder

できるだけ大きなクラスタを作りたい気持ちになるので、ある一種類のコンピュータに注目して、それを頑張って繋げることにした。まだ繋げていない二つのコンピュータを全探索し、それを繋げるための手数を計算する。これは、最初の実装では直線移動でX座標またはY座標を揃え、後から4回まで移動するBFSに書き換えた。4回という制限は探索時間の制約によるものである。

その後ケーブルを張るマスを見て、邪魔なコンピュータがあれば1マスどかしてみる。これで接続できた操作のうち、移動回数が最小のものを採用する。実際に繋げるのは最後で、今度は接続できるペアのうちスコアの増分が最も大きいものを優先して繋いでいくことにした。ケーブルのためのマスはマークしておいて、以降に採用した操作で邪魔されないようにする。

ところがまあ全然ダメ。クラスタのサイズを思うように大きくできなかった。代わりに操作回数も余っているので、注目するコンピュータを変えて何度もやってみることにした。まだ操作していないコンピュータの種類を全探索し、それぞれ新たに上のアルゴリズムを走らせてみて、最もスコアが大きくなったものを採用するというのをK回繰り返した。他のコンピュータを接続しているケーブルのマスは、BFSにおける移動の時だけは通過することができる。

これで93699点、1965ms。BFSの移動回数を5回にするとTLEが怖いので、このくらいが限界。10万点に届かなかったことはそれなりにショックだった。

実装中、先ほどのメールに返信が来たので、書類作成を少し進めたり詰まったところをメールしたりというやり取りを数回行った。

午前9時半に布団に入り、しばらくハーメルンを読んで午前11時就寝。

08/16(火)

午後6時半起床。ぼんやりTLを眺めていたら、周央サンゴさんによる志摩スペイン村の案件動画がプレミア公開されるのを知り、午後7時からはそれを見ていた。

実写!?と思っていたら貸し切りですごい。声も別録りではなさそう。バーチャルとは何だったのかという気持ちにもなるが、僕はこういうの大好きなタイプなので問題なし。

見ている途中にスマホに通知が来て、何かと思ったら午後7時半からミーティングがあるらしかった。一応任意参加と書いてあるが、せっかく起きているのだしと出席を決定。起き上がってPCの前に座り、1時間ほど話し合いを聞いていた。少しモチベが出たのでさらに1時間ほどインターン関連のコーディングを進めたが、先ほどのミーティングの内容を鑑みて今やるべきことが明確になっていないということに気づき、急遽明日1on1をお願いすることにした。

しばらくハーメルンを読み、午後11時半からCF #814 div.1。

Dashboard - Codeforces Round #814 (Div. 1) - Codeforces

Aから難しい。操作のコストをばらすことを考えると、操作の幅は2以下に限定してよいとわかる。この時操作回数がそのままコストとなる。さらに、左端から順に操作して0に揃えていくことにする。左端が同じ区間を使って2回以上操作することはないから、操作の列は「幅2の操作を一つずつずらしながら何度か行い、最後の要素が非ゼロなら幅1の操作で0にする」ものいくつかに分割できる。特に、幅2の操作によって最後の要素は全体のXORに等しくなる。この時点でO(n^2)のdpが書け、通った。

さて、遷移コストをよく見ると、基本的には分割後の幅と一致し、全体のXORがゼロの場合のみ1減っていることがわかる。あらかじめ答えにn足しておくと、この1減らす回数を最大化すればよいから、数列からdisjointな区間であってXORがゼロのものをできるだけたくさん取り出す問題になる。これは累積XORでZero-Sum Rangesっぽく解ける。ある位置以前から取り出した個数としてはこれまでの\maxを見ないとダメなのを忘れており、1WA。

Bも難しい。まず、連続する二つのフィボナッチ数を両方使わないようにしてある数をフィボナッチ数の和で表すためには、大きい値から貪欲に使っていけばよいということを確かめた。このような事実あったよなと思いながら計算していたが、ゼッケンドルフの定理というらしい。何回か見覚えがあって、フィボナッチ数の和と言ったら貪欲、みたいな繋がりだけ覚えていた。あとはフィボナッチ数列を降順に見て貪欲に当てはめていく。適当にやったらサンプルで落ちたものの、その時最大の値に当てはめるようにしたら通った。あまり自信はなかった。

Cはd=\gcd(n,k)のみが問題で、逆にd\mid nなるdを全探索できる。長さdの配列について値の書き換え・全体MAXの取得を高速に処理したいので、1点更新区間MAXのセグメント木を使った。2000ms弱。MAXを取得するとき、最初はpriority_queueを使っていちいち値がvalidか確かめるコードを書いていたが、MLEしてしまったため慌ててこちらに書き換えた。

この時点で20位ちょっととかなり良い位置にいたが、崖の先のDが解けなかった。Cartesian Treeを見て各位置に当てはめられる値の区間を求めるのはよいとして、その後どうすればいいのかわからない。左に寄せた場合と右に寄せた場合を考えてマージする方針が思いついたものの、左から貪欲する場合と右から貪欲する場合で区間の並べ方が異なってしまうためうまくいかなかった。最後にHallの結婚定理を考え、端を含む区間だけチェックしたら十分じゃないかと勘で提出した。これは当然のように落ちてしまった。

システスは全部通って33位、2943→2954(+11)。この辺りで安定しているのは正直かなり偉い。また致命的に失敗して2900台から落ちてしまう前にラッキーパンチが打てればよいのだが、なかなかうまくいかないものだ。Cは素数pについてd=n/pとなる場合のみチェックすればよかったらしい。O\left(q\sum_{d\mid n}\log d\right)が通るものと信じて、そこから先を全く考えなかった。

AHC013のシステスが終わっていた。実行時間の欄に2984msと書かれていてびっくりしたが、一応ギリギリ全ケースAC判定が出ている。順位は358位と少し落ちたようだ。パフォーマンス1302、レートは1910→1911(+1)。まあそんなものかという感想。そういえばコンテスト終了後のTLで感想戦を少し眺めていたのだった。サイズ100のクラスタを一つどころか二つ作れるケースもあったらしくてかなり驚き。

ほかのコンピュータに埋もれているものをちゃんと掘り起こしていないのが問題だろうか。seed 0のケースがまさに密だったので、そういうことをするべきだとわかってはいたものの、実装する気力がなかった。また気付いたこととして、すでに繋ぐケーブルを決定したコンピュータもそのケーブルに沿って動かせるというのを見落としていた。一度繋ぐと決めたものは二度と動かしていなかったのだ。

ハーメルンに戻り、最後の数話を読んで1作読了。「私は誰でしょう?」。

syosetu.org

非常に面白かった。タイトルやあらすじでは伏せられているが、主人公がギルデロイ・ロックハートであることはすぐ明らかになる。このキャラクターは原作において、死喰い人陣営でもないし敵でもないはずなのに余計なことばかりしてハリーたちの邪魔をするという、とんでもないストレス要因で、数多の二次創作でもいつも厄介者扱いされている。例えば僕が大好きなハリポタ二次創作「Game of Vampire」では、2年次の闇の魔術に対する防衛術の教授はアリス・マーガトロイドが担当し、ロックハートはそもそも作中に登場すらしていなかった。

この作品は、そんなキャラを再構成するというかなり変わった趣向。扱うキャラがキャラなので正直敬遠していたのだが、読み始めてみるとこれがかなり面白く、2年次終了時点の14話という短い話数でまとまってしまっているのが残念になるくらいだった。原作のようなあくどいことをすることもなく、誠実で、紳士的で、万能。原作と同じシーンをなぞりつつ、取る行動はどれもハリーたちを教授として教え導くようなものになっていて、気持ちよく読めた。

C++には以下のような罠がある。まだ眠る気にならなかったので、しばらくこれについてgccの実装を調べていた。

queueがデフォルトで512要素分のメモリを確保することを考えるとさすがにまずい。

週記(2022/07/25-2022/07/31) - kotatsugameの日記

queueはdequeによって実装されている。dequeのほうに問題があるようだ。以下のヘッダファイルがdequeを実装している部分である。2時間ほど格闘して何とか必要な部分が読み解けた。

github.com

まず、C++におけるdequeの実装はリングバッファではない。詳しい事情は忘れたが、イテレータが何らかの要件を満たすためだったはず。ではどうしているのかというと、要素をブロックに分け、ブロックの列を持っている。このことをすっかり忘れていたせいで読む苦労がかなり増してしまった。思い出して拍子抜け。

ただ、ブロックに分けた後の詳しい実装を知らなかったので、そこを知れたのはよかった。ブロックは型Tの要素512/sizeof(T)個、つまり512バイトの列である。この列は型TへのポインタT*という型を持ち、さらにdequeはブロックの列を持つのだからT**を管理している。まあここは今の本題ではない。

さて、dequeをデフォルトで構築した際の挙動を知りたい。このとき親クラス_Deque_baseのデフォルトコンストラクタが呼び出され、そこでは_M_initialize_map(0)が実行されていた。この0というのは当然最初の要素数だ。_M_initialize_map(n)では、nを1ブロック当たりの要素数で割って切り捨てた値に1足した分のブロックを生成する。なぜ切り上げではないのかというと、endを表現するためだろう。

列の終わりがブロックの終わりとちょうど一致した場合、endはそのブロックの末尾を指しそうなものだが、通常それは次のブロックの先頭と同じ意味であり、かつブロックが空間的に隣接しているとは限らないためメモリ上の位置としては一致しない。同じ位置を表現するイテレータが2種類あってはまずいため、ブロックの末尾へのイテレータは徹底的に避けられている。よって次のブロックの先頭を指す必要が生じるため、余計に一つブロックを作る必要があったということ。

これが要素数0の場合にも効いてきて、空のdequeであっても1ブロック生成されてしまい、先ほど述べたように512バイト分のメモリを消費する。上で引用したように512要素分のメモリを確保すると思い込んでいたのは間違いだったが、ただし512バイトであってもint型に直せば128要素分だから、大量にdequeを作るとまずいのは変わらない。

思ったより時間がかかって、睡眠時間がかなり削れてしまった。明日は朝からSRMがある。午前6時過ぎ就寝。

08/17(水)

午前9時半起床。SRMなんて寝てたほうがマシ説はあるが、ここは敢えて強い意志で参加……ということで気合いで布団から這い出て、午前10時からSRM835。

https://community.topcoder.com/stat?c=round_overview&er=5&rd=19389

Easyは列が2本あるのはフェイクで1本ずつ揃えられる。どの要素がどの位置にいて欲しいか決めて辺だと思うと、ループがいくつか作れる。このループに沿ってもう一方の列の適当な位置と交換し続けることで、ループの長さ+1手で揃えることができるため、全体ではNにループの個数を足した回数の操作が必要。ところが長さ1のループは無視してよいため、ループの個数は高々N/2となる。片方の列が3N/2手で揃えられたため、両方の列に対して行って終わり。

Medは周期Pを小さいほうから全探索。同じ文字だとわかる箇所をグループに分けると、文字数をNとしてサイズ\lfloor N/P\rfloorのグループとサイズ\lceil N/P\rceilのグループがそれぞれいくつかずつできるため、dpで割り当てを決めて復元。高速化というよりは実装の短さを求め、このdpはbitsetで行った。

Hardは間に合わず。普通に考えると状態は「重い可能性のあるもの」「軽い可能性のあるもの」「どちらかわからないもの」「本物」がそれぞれいくつあるかという組で表現できる。これを丁寧に確認すると、実は前半分が0の場合と3番目が0の場合しかないことがわかるため、それぞれメモ化再帰できる。まず前半分が0の場合、これはO(N)通りしかないから、毎回左右の皿にどちらかわからないものをいくつ乗せるか全探索できる。3番目が0の場合にどうすればいいのかわからず、時間に追われて適当に書いて提出したものの、コンテスト終了直後にダメなケースを見つけて終わり。

チャレンジフェーズは何もせず。なぜか自分のHardも落とされなかった。システスは阿鼻叫喚で、自分のHardが落ちるどころかそもそもHardのAC数が1だったほか、EasyもMedも大量に落ちており、結果的にそれらを両方通しただけで6位まで浮上できた。2934→2977(+43)でhighest。いよいよTargetが見えてきて震える。

しばらく日記を書いて午後1時からMulti-Universities Camp。当初は予定されていなかったコンテストで、いつの間にか生えていた。加赛(プレーオフ)と書いてあるが5回目のお誕生日コンテストの詫びコンテストという認識でよいだろうか。

お誕生日コンテスト。とにかく酷いセットだった。このCampは本来有料だと以前に言ったが、お金を取っておいてこのセットを出すのはさすがにヤバい。

週記(2022/08/01-2022/08/07) - kotatsugameの日記

"蔚来杯"2022牛客暑期多校训练营(加赛)_ACM/NOIP/NOI/CCPC/ICPC算法编程基础集训_牛客竞赛OJ

普段と違う曜日なのでチームメイトはあまり参加できなさそうな雰囲気を出していたものの、蓋を開けてみればほぼずっと3人揃っていた。偉い。ただし結果は全く偉くなく、チームでHMLEJKの6完。自分はHMEを解いたが、全部簡単枠。その後3時間ほどCと格闘して解けず、残り時間はGを見て解けず。ダメダメ。

一応Eについて言及しておこう。n人がゲームを行う。人1から順番にゲームを抜けるかパスするかを選んで進んでいく。人ij人目にゲームから抜けたとき、その人はスコアa_{i,j}\gt 0が得られる。最後の人まで回ったらまた先頭に戻って、一周全員がパスする(元の問題ではラウンドという概念が使われていたが、こう書いても結論は変わらない)か全員が抜けたら終了。このとき最後からp\le n番目に抜けた人がいればその人のスコアは負になる。それぞれの人が最後に得られるスコアを最大化しようとした場合、いったいどうなるかという問題。

まず、残りp人になった段階で全員がパスをする。残りp人を下回った段階で、パスするよりどのタイミングであっても抜けたほうが明確に得になるからだ。同様のことが残り2p人、3p人、……と続いて、結局ゲームから抜けるのはn\bmod p人だけだとわかる。

特にn\bmod p人目に抜けるチャンスが来た人は必ず抜ける。このことは、例えばパスが何回も行われた場合を考えるとわかりやすい。自分がパスしたらゲームが終了するとなると絶対にパスをしないので、そこから逆に考えていくと、常に「自分がパスしたら次の人が抜けてしまう」ことが言える。この「次の人」の存在はゲームから抜けられない人がn-(n\bmod p)\ge 1人いることから従う。

なんと同様のことが(n\bmod p)-1,(n\bmod p)-2,\dots,1人目のすべてについて言える。考え方も同様で、先ほどと同じくn-(n\bmod p)\ge 1から「自分がパスしたら自分の次の人から連続で抜けて行ってしまう」ことが「自分が抜けるタイミングが失われる」のと等しくなり、パスするだけ損だとわかる。

TLでしばらく感想戦を眺めた後、午後7時から1on1。昨日お願いしていたもので、2時間ほどミーティングしていた。内容はともかくとして、今日の自分の態度はかなりひどかった。眠すぎて一瞬意識を飛ばしたタイミングが数回あったのだ。弛んでいる。眠気に耐えられないのなら睡眠時間を確保するよう努力しなければならない。

疲れ果てているのに眠りたくなく、しばらくYouTubeを眺めていた。その後日記を少し書き進めてみたものの、いよいよ眠気に耐えられなくなってすぐ切り上げた。午前0時半就寝。

08/18(木)

午前6時起床。夢を見た。

ハーメルンの更新をチェックして、流れで1作読み始め、そのまま読了。「Vtuber界を駆け上がりたい」。

syosetu.org

「輝きたくて」の作者の別作品で、そちらの世界設定を引き継いでいるようだ。登場人物も一部共通している。こういうの、スター・システムと言うんだったか。図らずも後日譚のようなものに触れることになって思いがけない驚きと嬉しさがあった。逆に、もっぱらそのような見方で読んでしまい、独立した作品とは思わなかった。

それを取り除いて考えると、内容は正直微妙。主人公の最初の印象がマイナスで、そこから話が進むにつれ改善しては行ったものの、プラスに転じる前に完結してしまった。完結というより作者が放棄したという印象だが、それでも最終話が投稿され内容に決着がついているのはすごい。そこで明かされた事実により主人公に対するモヤモヤが解消されたので、読後感は良かった。

もう1作手を出してしばらく読み進めた後、正午前に寝落ちして二度寝に入った。次は午後6時起床。

布団に横たわったままハーメルンを読み続けていたが、今日のCFが普段より1時間早いことを知り、うっかり出遅れないよう起きておくことにした。午後9時頃に布団を脱出。

昨日のSRMのHardを通した。コンテスト中に何が最適かわからなくなってしまった部分だが、実はこれも全探索できるらしい。重い・軽いそれぞれ何個左右の皿に乗せるか、で4重ループ。ただでさえ定数倍が良いうえ、そもそも重い・軽いは最大でN/2個しか存在しないため、計算回数は見た目より大幅に少ない。

ところで、Easyで大量に落ちていたのは出題側のミスかもしれないという話が出ていた。最大で長さ6000程度のvectorが答えになるが、ジャッジがそのような長さのvectorに対応していないのではとのこと。実際、ACコードに適当な操作を付け加えると落ちてしまったらしく、暗雲が漂う。

食事してシャワーを浴び、午後10時半からCF #815 div.2。

Dashboard - Codeforces Round #815 (Div. 2) - Codeforces

Aは多くても2手で達成でき、0手の判定は自明だから、1手でできるかの判定が問題となる。一方の分母に掛けるのともう一方の分子に掛けるのは同じ意味なので、分子に掛けると固定してよく、式変形すれば掛ける数が整数になる条件が出る。つまり、adbcの間に約数・倍数関係があればよい。この関係は、どちらかが0の場合は必ず成立。

Bは難しかった。lrのどちらかを全探索する方針を考えたが上手くいかない。しばらく考えて、\max(a_1,\dots,a_{l-1},a_{r+1},\dots,a_n)\max(a_l,\dots,a_r)のどちらかは必ず\max aに等しいということに気づく。もう片方が2番目の最大値になって、同様のことが\minでも成り立てば明らかに最適だな、と考えて、実際に達成できることに気づいた。aの要素のうち大きいほうから二つ、小さいほうから二つに注目すると、それらをちょうど一つずつ含むような区間[l,r]が必ず取れるのだ。このことに気づいて、してやられたなと感じた。

Cは初手以外1を一つずつ0にしていくことができる。初手を全探索。

Dも難しかった。インデックスiの次にjが来られる条件を考える。これはa_i\oplus j\lt a_j\oplus iである。二つの数の大小関係がどのbitで決まるか固定すると、その桁だけ見たとき(a_i,i)=(0,0),(0,1),(1,0),(1,1)から(a_j,j)が一意に定まる。またそれより上のbitはa_i\oplus ia_j\oplus jで一致するから、これらの情報をキーにして値のセット・取得を行うことでdpができる。情報が各iに対し決め打つbitで30種類、またセット・取得にO(\log n)かかる。

解けたと信じて投げたらD1は500msだったがD2でTLEした。冷静になるとn\le 3\times 10^5\logと定数倍30が付くのはヤバい。しばらく考えてもどうにもならなかったので、219の位より上でijが必ず0になることを利用し、絶対に利用されない・存在しないキーを見ないという定数倍高速化を入れて投げてみると、1950msでギリギリ通った。

Eは解けず、34位。Dはbinary trieで情報を持てばbitを決め打ちつつキーによる値へのアクセスができるので\logが落ちるらしい。なぜ気づかなかったのか。ちなみに自分の解法は、システス後は1996msになっていた。

Eはなんと、種類数を減らす場合が必ず2手で達成可能のようだ。鍵アカウントのツイートを参考に詳しい証明を書く。まず一番左上のマスを覆う同じ値の正方形を用意し、種類数が足りなくなるギリギリまでサイズを大きくしていく。これでkにできなかった場合、今作った正方形のサイズをxとするとx\lt nなので、(x+1,x+1)のマスを覆う先ほどと同じ値の正方形が用意できる。ここで種類数がkになる可能性はあるが、それより小さくなることはない。

またサイズを大きくしていくが、今度は左上の方向に伸ばす。ここでもピッタリにできなかった場合を考えよう。サイズ1のときは種類数がkより大きく、サイズx+1まで伸ばし切ったときはxの決め方からkより小さくなる。さらに、サイズを1伸ばす度にたった2マスだけ新たに覆われることになるから、種類数の変化は毎回-0,-1,-2のどれかである。つまりどこかのタイミングで種類数がk+1からk-1に変化したことになる。

k-1のときのサイズで操作する。もし今の二つの正方形が完全には被っていなかったら、片方の値を変えることで種類数kが達成される。後者の正方形が前者の正方形を覆ってしまった場合、前者の正方形で操作するのを止め、代わりに後者の正方形のうちどこか1\times 1マスを別の値にすればよい。以上で2手での操作が構築できた。

つまり1手で達成できるか判定すれば十分。コンテスト中はここすら思いつかなかったのだが、正方形のサイズを決めた後、どこで操作すればどれだけ種類数を減らせるかを2次元imosで計算するとO(n^3)で行える。

さらにハーメルンを読み進めて1作読了。「マエリベリー・ハーンと賢者の石」。

syosetu.org

非常に面白かった。クロスオーバーで、腹に一物抱えてホグワーツに入学したマエリベリー・ハーンが1年で才能をどんどん開花させつつ、賢者の石を巡る攻防に巻き込まれたりする話。原作の展開をなぞりつつ、それを一段上から観測する感じがワクワクして楽しい。そうやって原作の間に明確に力の差があるような描き方は好きだ。

1年次終了時点でひとまずまとまってはいるものの、その後も投稿される予定ではあるらしい。しかし3年以上も間が空くと厳しそうだな、と思って作者の別作品を調べると、「雛森「シロちゃんに『雛森ィィィィ!』と叫ばせたいだけの人生だった…」」が見つかった。実はこれも80話ほど読んでいて、本編が完結しおまけに入ったところで止まっている。そういう状態だから日記では言及していなかったのだ。ずっと活動を続けていらっしゃるのなら、気長に待てば本当にそのうち更新されそうだなと考えた。

syosetu.org

TCB50に出た。日曜日終了なのでここに感想を書く。

https://techful-programming.com/user/event/3196

5問目まではよい。6問目はかなり難しかった。2番目のサンプルを手で試し、どうやら数回操作した後は周期2になるらしいと気づいた。一回の操作はO(N^2\log N)でシミュレートできる。2回の操作で周期に入ると思ってコードを書いたが、正当性が示せておらず怖かったので、念のためN回は操作することにした。出してからやっぱり計算量がまずいかと思ったものの、最大でも1.5secで通った。この問題で理論値を取るためには12分以内に提出しなければならない。自分は10分半かかったので、かなり危なかった。

7問目は何も考えずO(NA^2B^2)のdpを書いたら手元で3secちょっとかかってしまった。そのまま提出してしまうことも考えたが、少し睨みつけると遷移が区間加算だと分かり、計算量からBを一つ落とすことができた。

8問目は、すべてのKに対する答えを計算しておいて、更新の度に差分更新することを考えた。書き上げて一度は提出しそうになったが、よく見ると12で交互に更新された場合まずいことに気づき、直前で踏みとどまった。冷静になると、10^5/K回の計算で答えを直接求めることもできる。よってK\ge 10^2の場合はそちらを使い、K\lt 10^2の場合は差分更新することにした。これだと更新の度に見るべきペアが高々2\times 10^2個になるため、間に合う。

9問目は今度こそすべてのMに対して答えを計算しておく。適当に見積もると、M\ge 2\times 10^5の場合は\sum A(A+1)/2が答えになるようだ。それ以外のMについては、kM\lt A\lt(k+1)MなるAがどちらに寄せるべきなのかは閾値が式変形で出せるし、かかるコストも適当に累積和を取ったりして計算できる。見るべき(M,k)の個数は調和級数になるので、十分少ない。

10問目も難しかった。今のKがいくつで、操作はi回目、という状態を考えるとdpはできる。これまでP=1\dots Kでは操作したことがないので、それらで操作してKを小さくするか、あるいはK+1\dots Nのうちこれまでのi回で使用しなかったものをPに選びKを据え置くか。これは当然O(N^3)である。P=1\dots Kで操作した場合の次のKをあらかじめ求め、重複を取り除いてK=1\dots 3000に対してどれくらいの遷移回数になるか計算してみると、2\times 10^6を超えていた。これではお話にならない。

しばらく考えていると、最初の1手でK\lt N/2になることに気づいた。再度先ほどの同じものをまとめた場合の遷移回数を計算すると、今度はK=1\dots 14996\times 10^5弱まで減ることが分かった。ここにN=3000を掛けても間に合うのではないか。\bmodが変数なので、余りを取る操作を引き算で丁寧に実装し、手元で実行してみると1secを切った。提出すると2sec程度で通った。この問題も理論値ボーダー24分のところ22分半だったのでギリギリ。

久しぶりの理論値だった。時間は75分弱か。6問目をエスパーで通したのと10問目が非想定っぽいので少し釈然としない。終わった後しばらくこれらについて考えていた。

6問目は2回目以降が周期2になるらしい。これを示す。まず問題を、行列aを行ごとに降順ソートするのと列ごとに降順ソートするのを繰り返していると見なす。このもとで、2回操作を行うと任意の要素a_{i,j}についてa_{[1,i],[1,j]}\ge a_{i,j}が成り立つことが示せる。これより以降どの向きにソートしても変化しなくなるというわけ。

証明しよう。以下、aは2回操作した後の行列である。まず、a_{i,j}は1回目のソートによってj列目に動いた。つまりこのとき、同じ行の[1,j]列目の値はa_{i,j}以上だったことになる。まったく同様のことがa_{[1,i),j}の各要素に対しても言えて、2回目のソートの結果a_{[1,i),j}\gt a_{i,j}なので、結局、1回目のソート後に[1,j]列目の値がa_{i,j}以上だった行はi個(以上)存在したのだ。これを列ごとに見ると、[1,j]列目にa_{i,j}以上の要素がi個以上ずつ存在すると言えて、それらを列ごとに降順ソートするのだから当然a_{[1,i],[1,j]}\ge a_{i,j}となる、というわけ。

10問目はPに何を選んだかが場合の数に関係ないのを見落としていて、さらに遷移の係数も掛け忘れていたため、奇跡的に答えが合っていたらしい。その係数を求める必要がないのなら、K1\dots Kで割ったあまりとしてあり得る数を考えれば一瞬だった。実は0\dots\lfloor(K-1)/2\rfloorになるので、これまた遷移が区間加算で書ける。

ゴミを出した後、昼前まで日記を書いていた。布団に移動して少しラノベを読み、午前11時半就寝。

08/19(金)

午後9時過ぎに目を覚まし、ちょうどyukicoder開始少し前だったので布団から出て参加した。

yukicoder contest 357 - yukicoder

Aはよい。Bはstrictlyである必要がないため色数をできるだけ揃える発想が出て、必ず達成可能だとわかる。先頭から3人ごとにRGBを一つずつ使えばよいのだ。矛盾しない範囲で適当に証言を増やして実装した。Cは枕の横の長さを決めると縦の長さが区間になって、\sumを閉じた形にすれば置き方を一気に求められる。Dは石の数が無限であるような山が少ないほうから考えてみると規則性が見える。

Eのsolvedが少なかったのでFに進んだ。FはAを昇順ソートすれば\sum_{i\lt j}-(A_j\bmod A_i)を求める問題になって、BITを使うとO\left(\frac{\max A}{A_i}\log\max A\right)で計算できる。今Aはdistinctなので、調和級数になる。Gは根付き木にして、各部分木の根が「孤立している」「長さ2以上のパスの端点である」「パスの内部の点である」の3状態を持つ木dpを行えばよい。

Eに戻ってきた。実験すると、最初の順列から操作1と操作2を交互に繰り返し、初めの状態に戻ってくるまでにかかる操作回数が答えになるらしい。特にその最後の操作は操作2になる。操作1と操作2を組にすると順列の変化が置換として書けて、グラフだと思ってループの長さを見れば、各位置について何回の操作で元に戻ってくるかがわかる。これの最小公倍数が答え。素因数分解して求めた。

Hは解けず、7完。

Eの解説で集合Sの要素が相異なるというのがよくわからず、日記を書いているときにTLで質問した。xyxyx\dotsyxyxy\dotsという列が、長さ2m未満で{\rm id}にならないことを示したい。長さが偶数の場合はmの取り方から従う。奇数の場合について、例えばxyx={\rm id}となった場合、両辺に左右からxを掛けることでy={\rm id}となってしまうため矛盾するようだ。

しばらくハーメルンの更新を読んでから、R言語について少し調べていた。指導教員の先生からの頼まれごと。よく知らない言語のよく知らない型を弄るので大変だった。これには3時間くらいかかっていたらしい。その後TCO Finalsの書類作成を進めて朝方に。

日記を書いて布団に入った。うっかりラノベを少し読み進めてしまい、就寝は午前10時だった。

08/20(土)

午後0時半起床。ありえないくらい眠い。昨日眠気が来ないのをいいことに好き勝手夜更かししたのが馬鹿だった。後悔しても睡眠時間は戻ってこないし、実際起床に成功しているのだから今後も同じことをやってしまうのだろう。人間は愚か。

午後1時からMulti-Universities Camp 10回目、最終回。

"蔚来杯"2022牛客暑期多校训练营10_ACM/NOIP/NOI/CCPC/ICPC算法编程基础集训_牛客竞赛OJ

チームでIHKGDEFJの8完、5位。自分はGFJを解いた。

Gはn\le 4\times 10^4山のNimで、ただし山の石の数が常に広義単調増加になっていなければならないという制約を足したゲームを考える。一つの山に対する石の数が最大m\le 10^{12}だとしたとき、後手が勝つ盤面を数え上げよという問題。ほとんど蟻本278pに載っていたゲームと同じであることに気づき、とりあえず勝敗判定ができるようになった。

山の石の数を0\le a_1\le\dots\le a_n\le mとおき、a_1-0,a_2-a_1,\dots,m-a_nn+1個の非負整数に総和がmになるように値を割り当てる問題だと捉える。このうち固定された\lceil n/2\rceil個の値のXORが0になることが後手勝利の条件。k=39\dots 0について、2^kがいくつ余っているかでdpすると、何個使いどこに割り当てるかというのが遷移になる。使ってもなおn+1個以上余っている場合はもう取り返しがつかないため、無視することができ、dp配列はそれほど長くならない。

40回の遷移を行うが、それらはまったく同じ多項式を畳み込んでいる。よってこの多項式さえ求められればよい。つまり、0\le i\le n+1について、2^ki個使ううち偶数個を固定された\lceil n/2\rceil個に割り当てるような方法を数え上げたい。深く考えずにO(n^2)を書き、ちょっと定数倍高速化したら通った。

Fが100チーム以上に通されているのに全然わからず困っていた。どうしても考察が先に進まず、気分転換しようと問題文を改めて読み直したら、誤読に気づいた。慌ててPCを借りて通した。

Jは長さNの数列ABが与えられて、(i,j)を選んでA_iA_jをswapする操作をすべての1\le i\lt j\le Nで行った後A=Bとなるようにできるか、判定+構築をする問題。操作の順番は任意。A=1\dots NBを順列だと思うと、Bの転倒数とN(N-1)/2の偶奇が等しいことが必要条件。実は十分条件でもあるのではないかと考え、実際に構成した。

A_i\ne B_iなるiが存在する場合、このiを含むような操作を全部行った上でA_i=B_iとなるようにできるため、それを行う。これでN\leftarrow N-1とできる。残っているN個のiすべてでA_i=B_iの場合、現在の転倒数とここからN(N-1)/2回操作したときの転倒数の偶奇が等しいことがわかる。つまりN(N-1)/2\equiv 0\pmod 2よりN\equiv 0,1\pmod 4

チームメイトがN=4の場合の操作列を一つ構築してくれた。(1,2),(3,4),(1,4),(2,3),(1,3),(2,4)。これを適切に拡張すればN\leftarrow N-4とできるのではないかと考えると、実際に可能だった。

i=1,2,3,4の操作を消費するとして、残りN-4個のuについて(i,u)を全部使い切りたい。N-4\ge 2のとき、u,vを取って(1,u),(2,v),(1,v),(2,u),(3,u),(4,v),(3,v),(4,u)とすることで、[1,2,3,4]\rightarrow[2,1,4,3]としつつ(i,u),(i,v)を使いきれる。今N-4\equiv 0,1\pmod 4なので、これを偶数回繰り返すことで残りのuが0個または1個になる。0個の場合は先ほど紹介した6回の操作でOK。1個残った場合は、(1,2)の代わりに(1,u),(1,2),(2,u)を、(3,4)も同様に展開することで、(i,u)を使い切りつつ6回の操作と同等のことができる。

以上を丁寧に実装してAC。最初にBを順列にするとき、元の列に同じ要素が複数出現するなら、適切にswapすることで転倒数の偶奇を任意に定めることができるため、必ず揃えられるようだ。

これまで特に触れてこなかったが、Multi-Universities Campに参加するとチームに対してレートがつく。1000スタートでそこそこ順調に伸ばしていたものの、水曜日のプレーオフでついに減らしてしまったりとそううまくはいかず、最終的に10回のratedで2774を記録した。赤まであと26だったので残念。別の日本チームはわずか5回で赤に突入しており、あまりにも強い。

japan1000000007的比赛主页

実は午後3時頃に両親が来ていた。僕がコンテストに出ている間はありがたくも部屋の掃除などをしてもらっていた。コンテスト終了後すぐ外出。仙台駅まで出て、駅ナカの飲食店で食事した。今日は牛タンではなく、普通の定食。アルコールも頼んだものの、薄すぎてそれらしい味は全くしなかった。

ヨドバシカメラやスーパーに寄って少し買い物をし、帰宅。午後9時からARC146に出た。僕の序盤の様子を見たいとのことで、コンテストが始まったタイミングではまだ両親が部屋にいた。

AtCoder Regular Contest 146 - AtCoder

Aはまず桁数が大きい数を使いたくて、タイブレークは辞書順で大きいほうを使うのでよさそう。つまり使う三つの数は確定する。このあたりでコーディングを始めたが、しかしそれを並べるのが思ったより難しいことに気づいた。3!全探索するコードを書いて提出。無事ACしたのを見届けて両親は帰っていった。

Bも簡単。上のbitから貪欲に、そこを立てられるなら立てていく。ある数をインクリメントすることでbitを立てなければならなかった場合は、インクリメント回数が少ないものから順に使えばよい。より下のbitが全部0になるため、どれを選んでも以降に影響しないのだ。ここまで10分足らずで通し3位に浮上した。順位表を見ているだろう両親も喜んでくれるかな?と思いつつ、意気揚々とCに進んだ。

しかし以降110分間何も解けず、結果は2完178位。残り20分を切った段階で諦めてDに移り、こちらはすぐ見えたので書き始めたのだが、さすがに間に合わなかった。600点が解けない自分というのを認めたくなくて、ずっとしがみついていたのだった。

コンテスト終了後3分ほどでDが書きあがり提出するも、WA。これは軽微なミスで、追加15分くらいで今度こそACできた。この15分の間にTLを見たりしていた記憶があるので、字面よりは惜しかったはず。

数列のインデックスごとに条件となるXまたはYを集め、それぞれ「(その値より)大」「(その値と)等しい」「(その値より)小」の3種類の状態を持つ。値を昇順にソートすると、この3種類が順番に現れることになる。初期状態はすべて「小」で、そこから値が1であるような条件について「等しい」に切り替えるのがスタート。以降BFSのように連鎖的に状態を切り替えていく。

遷移は、同じ条件から得られた組で状態を揃えるのと、同じインデックスで値のソート後に一つ隣にあるものに状態を伝播するもの。伝播元は必ず「大」または「等しい」なので、値が小さい条件に状態を伝播する場合は常に「大」になる。また、「大」から値が1だけ大きい条件に「等しい」として伝播する必要もある。後者を忘れていたというのが、コンテスト終了後に出したWAの原因だった。

最後にインデックスごとに条件の状態を見て、すべてを満たす中での最小値を取る。等しいものがあればそれ、そうではなく「大」があればその最大値に+1したもの、残りは1。こうして取った値がMを超えていた場合、答えは-1になる。

Cは解説冒頭を数行読んでupsolveした。XORという文字列を見た瞬間桁ごとに考えて帰ってこられなかったが、個数を見るのが正解とはびっくり。またすべての要素に+2^NすることでSが一次独立という条件になるようで、ここからはよりダイレクトに個数しか考えなくてよいことがわかる。パターンマッチングした人間を陥れる上手い問題だった。

午後11時半からCF #816 div.2。

Dashboard - Codeforces Round #816 (Div. 2) - Codeforces

Aはいかにも難しそうだったがサンプルを説明する画像がすべて。Meganに(1,1)を通るようなルートを進んでもらって、Stanleyは(1,1)のポータルから(n,1)または(1,m)に飛ぶのが最適っぽい。パッと思いつかなかったので、証明はせずに進んだ。n=1またはm=1のときは飛ばなくてよいので、そこを丁寧に処理するか、あるいはそれをしなくても(n,m)=(1,1)だけ特別扱いすればよい。このケースがサンプルにあるのは優しさ。

Bはkb\le s\le kb+n(k-1)が必要条件で、s-kbk-1ずつ貪欲に割り振ってよい。ただしkbについて、うっかり1要素に全部押し付けてしまうとa\le 10^{18}を満たさなくなってしまう可能性があると考え、できるだけ均して割り振ることにして、そういうことができないn=1の場合を特別扱いした。しかし今改めて確認するとs\le 10^{18}だったので、このような面倒なことは考えなくてよかったらしい。

Cはa_i\ne a_{i+1}であるような位置を考えればよい。答えにどれだけ寄与するかがiごとに計算できて、更新する度両隣について計算しなおす。Dは桁ごとに見る。0の位置を確定させた後、1はできるだけ後ろに押し付ければよい。制約で与えられた条件が矛盾しないことが保証されているため、あまり細かいことを考えなくて済んだ。Eは飛行機に乗る回数でループして、地上の移動は多始点dijkstra、飛行機による移動はCHT。直線の傾きが単調なので線形でできるはずだが、面倒だったのでうしさんの動的CHTを拝借し、1200msで通した。

Dynamic-Li-Chao-Tree | Luzhiled’s Library

Fは謎。xyをそれぞれ2手で求められる。xについて書く。基本的な方針として、yに影響しないような図形を考えたい。これはY軸方向に周期1を持つ、つまり1だけずらしても形が変わらないような図形である。それでいてかつ、xを区別できると嬉しい。ここで、底辺m、高さ1の直角三角形を縦に大量に積み重ねることを考えた。直角が左下にあるとすれば、xが大きくなるにつれて共通部分の面積は小さくなるため、逆に面積から式変形でxが求まる。

ただしこれでは、多角形に自己交差が生まれてしまう。よって左端に縦に長い長方形を作り、三角形たちをくっつけることにした。長方形の中に正方形が被ってしまうと困るが、最初に長方形だけで聞けば対処できる。以上で2手。出したら通った。

一時間弱で全完できて気分が良い。システスも全部通り2位だった。Aの解説にあった証明が結構面倒くさくて、やはり直感で突き進むことが求められていたのだなと思った。Fはジャッジを作るのが大変そう。途中、負号付き0が正しく処理されていなかったとかでリジャッジが入っていた。

ARCのコードゴルフ。Aの現状の最短コードを読み、少し縮めて提出したところ、WAになった。すでにafter_contestが追加されていたようだ。コンテスト中は使う三つの数を辞書順で降順ソートしたら通ったらしく、参考にした最短コードもそれを実装している。正しい解法で短くできる気がせず諦めた。以降の問題は手付かず。

しばらくYouTubeを眺めてから午前3時半に布団に入り、ラノベを読み始めた。

1冊読了。「ありふれた職業で世界最強」12巻。前の巻の内容をあまり覚えていなかったが、この巻はバトル・バトル・またバトルという感じだったので特に問題なし。しかし主人公は早々に別行動に入り、ヒロインたちだけが描かれていたので、それはちょっと物足りなかった。ただ、その分次の巻は主人公とラスボスが思う存分バトルしてくれそう。しかも最終巻らしい。実はこの巻が発売されたのは今年1月で、13巻の発売がすでに9月末と決定している。非常に楽しみである。

もう1冊手を出し、ほんの少し読み進めてから就寝。午前6時前だった。

08/21(日)

午後0時半に目覚ましで起きた。45分からyukicoderでNPCA 合宿コンテスト。

NPCA 合宿コンテスト - yukicoder

ペナルティがないので全問提出欄でコーディングした。10分足らずで全完、ACDEのFAで2位に100点差を付け優勝。感想は特になし。午後1時頃また寝た。

午後6時半に改めて起床。日記を書いて午後9時からABC265に出た。

AtCoder Beginner Contest 265 - AtCoder

Aを読んで意気揚々と言語にdcを選択、(N\bmod 3)\times X+\left\lfloor\frac N 3\right\rfloor\times Yを提出しWA。なぜWAなのかわからず、手元でサンプルを確かめてようやく気づいた。面倒だったのでC++で改めて実装した。BCはよい。

Dはxを決めるごとに二分探索を3回してy,z,wを決めるのが思い浮かんだが、なぜかこれがO(N(\log N)^3)だと思い込んでいた。少し考え、iを降順に見てそこがxyzになれるかをそれぞれ求めたものの、これも結局二分探索を3回している。

Eで少し詰まった。ワープを使った回数を考えればよい。O(N^3)のループの内側でその座標がvalidか調べるためにO(\log M)かけている。計算量が想定解より悪いと思って恐る恐る提出したが、100msもかからず通った。定数倍が良いからだろうか。

Fはかなり時間をかけた。最初d(p,r)+d(q,r)\le Dだと思い込んで実装してしまった。これは斜め方向への区間加算になってimos法で解けたので、正しい問題も最初はそのようにして考えてみたが、なんと斜めが2種類も出てきてしまった。そもそもO(ND^2)なうえ場合分けも大変そうであまりに嫌だったので、別の方針を考えた。

三角不等式よりd(p,q)\le d(p,r)+d(q,r)であるが、このうち特に等号が成り立つrのみをまず数える。等号が成り立たない場合というのはr_i\lt p_i,q_iまたはr_i\gt p_i,q_iとなる場合だから、このようなiが存在するケースについては、r_i=p_iまたはr_i=q_iとして数えておいて、後からr_iを遠ざける場合の数を試せばよい。p_i=q_iの場合はまた特殊なので、このようなiはカウントしておく。

まず、r_i=p_iまたはr_i=q_iとなるiの個数と、d(p,r)を持ってdp。遷移が単なる横方向の区間加算になるからO(N^2D)で解ける。次に、d(q,r)=d(p,q)-d(p,r)を求め、余った\min(D-d(p,r),D-d(q,r))r_iを遠ざけるために使う。ここでp_i=q_iなるiをどれだけ使うかも決めるとO(N^2D)通りあって、それぞれO(1)で解ける。具体的には、まずp_i=q_iのところでr_iを正と負の方向のどちらかにずらした後、それでも残った距離をどこにどれだけ割り振るかが重複組み合わせになる。

Gは遅延セグメント木。てっきり(S,T,U)が順列をなすと思い込んで実装したら、サンプル2が合わなくてひっくり返ってしまった。区間について、そこにどの作用素が作用したら転倒数はどの値になるかというのを持っていたのだが、書き直している最中に順列でなければこれはうまくいかないと思い、解説にあるような順序付きペアを数えて持っておく方法にした。実は元の方法でもうまくいって、作用素fを作用させた場合は新しくgに対応する値として元のg\circ fに対応する値を見ればよい。ただ定数倍が悪いので、1500ms程度かかっている。

Exは解けず。パス付きのNimだと思えて、NimのほうはXORが高々31なのでどうにでもなりそうだったが、パスの回数を数えるのが不可能だった。7完27位。

コードゴルフ。Aはdcで適当に。BはPerl。DもPerlで、これは負け。他は手付かず。

午前0時、TCB50が終了した。単独理論値で見事に優勝。

朝まで日記を書いていた。今週3回あったMulti-Universities Campの部分をすっかり埋め残してしまったが、それ以外は一通り書いた。このあたりで切り上げて、また明日完成させることにする。

途中集中が切れたので寿司打をやっていた。昨日ヨドバシカメラスマホアームを買ってきたので、手元を撮影してみた。音が小さい。

久しぶりに新刊ラノベや文庫本をチェックして19冊購入した。池袋ウエストゲートパークの16巻の文庫が9月初めに出るらしい。何巻まで読んだかなと思って記録を漁ると、なんと15巻を買っていないということが判明した。そういえばラノベだけ見て文庫本をチェックしていなかった時期があった気がするから、そのせいだろうか。それにしても、リアル本屋で棚を眺める機会が極端に減ったとは言え、1年もの間気づかなかったのには驚き。一緒に注文しておいた。

布団に入ってしばらくラノベを読み、午前10時半就寝。

週記(2022/08/08-2022/08/14)

08/08(月)

正午起床。起き抜け、喉に違和感があり、「やったか」と思った。コロナウイルスの今流行っている変異株では喉の痛みが初期症状らしい。しかし一方で、エアコンを除湿モードにしたまま口を開けて寝ていたようだったので、ただ乾燥しているだけという可能性もある。

とりあえず今日は家から出る用事もない。食事して午後1時からMulti-Universities Camp 7回目。

"蔚来杯"2022牛客暑期多校训练营7_ACM/NOIP/NOI/CCPC/ICPC算法编程基础集训_牛客竞赛OJ

チームでGKCFJA、自分はGとFを解いた。序盤の速度は非常に良く、確か30分ちょっとで5完してその時点では1位だったはず。しかし6問目が通ったのがコンテスト開始2時間後ですでに順位は下がり気味、その上それ以降1問も解けず、最終順位はこれまでのCampでも格段に低かった。

Gは文字列に対してその全体にマッチする最短の正規表現の長さと種類数を求めよというもの。基本.*で良いためギャグ。Fは、円状に並んだ数列から隣り合う要素であって「等しい」「足してxになる」のどちらかを満たすペアを削除する操作が、最大で何回行えるかという問題。aとペアにして削除できる数はx-aとペアにしても削除できるため、どちらを残すかなど考えず貪欲に操作してよい。

Eをサンプルからエスパーして書いてみたものの、出すと落ち、さらに手元で愚直を書いただけでも全然答えが違うことが判明して絶望。じっと考え込んでいるうちに眠気がひどくなり、30分ほど仮眠を取ったが改善せず、インターン先定例会で離脱するまで自分は使い物にならなかった。

午後4時半からインターン先定例会。特に何もなし。進捗もあまりない。勉強会は「イノベーションのジレンマ」という本に関してで、論理は理解できるもののあまり興味が持てなかった。企業経営の視点だけでなくエンジニア目線での話もされていたが、会社で働く人間としての自覚が薄いためどうにもふわふわした手触り。自分で何かを判断するようなことがないのが大きそう、と考えた。

母親から電話がかかってきて、これ幸いと帰省について少し相談した。元々明日からの予定だったが、起きた時の喉の痛みもあるし、コンテスト中の異常な眠気も怪しい。つい昨日、東京に行って数人とテーブルを囲み食事したという事実すらある今、帰省を取りやめにするべきなのではないか。結局その場で結論は出ず、明日起きた時判断する、ということになった。

日記を書いていたらへのkからDMが来た。日本最強プログラマー学生選手権の宿泊について。お互い前泊するつもりらしく、まだ宿を決めていない。このとき、二人分の宿泊費を合わせればパークハイアット東京という高級ホテルに泊まれるのではないかとのこと。実際は土曜日夜は値段が跳ね上がるため不可能なのだが、ともかく二人で泊まるというのはなかなか面白い行動に思えた。

しかしAtCoderのほうに問い合わせると感染症対策のためやめてほしいと返答が来た。当たり前すぎる……。

それでも泊まる宿は合わせることにして、しばらく相談を続けていた。いろいろ探すうち、なぜか予約サイトのポイントが大量付与されて2割引きで泊まれるものを見つけた。割引なしではまったく手が届かない値段だからかなり良いホテルのはず。そこに決め、予約した。

午後11時半、ひとまず週記を書き上げて投稿。先週分は校閲がされていないどころか日曜日の部分をこれから書く予定の参加記に丸投げしているので、内容が非常に薄い。とは言え間に合ったので、その解放感からしばらくYouTubeに意識を持っていかれていた。

校閲まで終わったのが午前2時半。上で予約したホテル代と調べた交通費をもとに日本最強プログラマー学生選手権の参加登録のフォームを埋めて送信した。その後はしばらくYouTubeを眺め続け、午前5時就寝。

08/09(火)

午後1時起床。喉の痛みはない。頭が多少ぼんやりしている気がする。

さて。喉の痛みは、昨日書いたようにただの乾燥である可能性が高い。また眠気がひどかったり頭がぼんやりするのも、最近睡眠不足だからという理由が考えられる。そもそも、日曜日に東京でウイルスをもらってきたとして、次の日いきなり発症ということはまずないはずだ。だから昨日今日の体調不良は別の原因であると言い切ってよいだろう。

しかし東京に行った数日後に帰省を強行するというのもまた躊躇われることであるのは間違いない。なのでやはり帰省は取りやめにしようという決断をし、両親に電話して伝えた。PCR検査で判断したいところだったが、軽く調べた感じお盆前で予約が完全に埋まっているらしい。

先週金曜日に参加したDMOJのYet Another Contest 4が終了していた。ここに感想を書いておく。

Yet Another Contest 4 - DMOJ: Modern Online Judge

P1は操作しても何も変わらない。P2はb_iについてb_j=b_i-1なるjがあるならi\rightarrow jとすればよく、なければb=b_iなるインデックスを集めて長さb_iのループをいくつか作るしかない。ここまでは簡単枠で、さあこれからだと思っていたら、なんと3問目以降1問も完答できなかった。

P3は2部グラフなら自明、そうでなくても3彩色可能だと考えた。実際、2部グラフだと思ってdfsで白黒に塗りつつ、もしどの色で塗っても同じ色に隣り合ってしまう場合は必ず白を使うと固定したところ、ちゃんと黒が隣り合うことはなく、また白で塗られた頂点だけ見たとき2部グラフになっていた。assertで確認した。ここを改めて塗りなおすことで3彩色が完成するため解けた、と思ったのだが最後の部分点でWAが出る。理由がわからないので、60点のまま飛ばしてP4に移った。

P4は苦行。縦横入れ替えることを考えると、横に切ると固定してよい。この時条件は、「行・列ごとの和がすべて偶数であること」「切った上半分だけ見たとき、列ごとの和がすべて偶数であること」と書き直せる。前者を見ることで操作の対象にするべき長方形の縦横が決まることもあるので、縦と横が決まるか決まらないか、都合4通りの場合分けをして、気合いでチェックする。チェックの方法を事細かに書く気はない。基本的に、どの場合でもまず切る行を固定して、それから条件を満たす長方形とその内部の奇数が存在するかを、適切に前計算することで見た。

とりあえず最初の部分点は通ったものの、その後全く先に進まない。コンテスト終了間際に勘違いに気づいたものの修正できるわけもなく、合計265点に終わった。直後からしばらくかけてP3、P4、P5のupsolveをした。

まずP3だが、なんと「2部グラフなら自明」と書いたところが間違っていたことが判明した。B以下で最大の2べきを2^tとすると、2^t2^t-1を交互に置くことで最大2^{t+1}-1が達成できる。自分はB0を置いており、しかもそこに何ら違和感を覚えなかった。3彩色の部分ではちゃんと2べきのことを考えていたのに、いったい何をしていたのだろうか。

ちなみにカクタスグラフは3彩色可能。より一般に外平面的グラフは3彩色可能であり、これは5月に受講した集中講義の課題にもなっていたが、四色定理でぶん殴る証明がある。外平面的グラフとは、1頂点追加してそこからすべての頂点に辺を伸ばしても平面的なグラフだと言えて、これを4彩色すると追加した1頂点の色は残りの頂点に塗れないため、その頂点を削除すると3彩色になっている。まあそもそも平面グラフの4彩色を得るのが難しいため今回の問題には関係ない。

P4。コンテスト中の勘違いとは、長方形の縦が決まっていないとき、わざと広く取ることで遠くの奇数マスを巻き込めることを完全に見落としており、近くで解決できないならダメだと諦めていたこと。しかしこれを修正しても全然通らなかったため、ひとまず愚直を書き、4通りの場合分けそれぞれを置き換えてどこが間違っているのか試してみた。これを書いてさえいればあと15点くらい入ったはず、と思いながら実装していた。

しかし相変わらずWAが出てよくわからない。しばらく苦しんでついにバグを発見した。なんと、行列の縦横を入れ替える部分のコードが間違っていたのだ。添え字ミスでN\times MM\times Nにするはずが逆になっていた。これを修正し、満点。基本のアルゴリズムは勘違いを修正して以降特に弄っていないものの、そもそもその勘違いの修正すらコンテスト時間内に修正できなかったのだから、満点は絶望的だったか。

P5はコンテスト中に見ることができなかったが、非常に簡単だった。Kを左から右に動かしていくことを考えると、K=(p_i+p_j)/2を境にijの順番が入れ替わる。またその位置を通る瞬間、標識は2通りになって、そのうち1通りは直前までのものだから、答えに+2-1すると考えられる。より一般に、(p_i+p_j)/2が複数のペア(i,j)によって一致する場合、各ペアの順序は全く独立だから、重複度をkとして答えに+2^k-1することになる。畳み込みで重複度を計算できる。

順位は33位、レートは2625→2475(-150)。最悪。なんだかDMOJが苦手らしい。問題が苦手だし、順位表が見えないというコンテスト形式も苦手。

午後2時に二度寝。寝ている間にGCJ Tシャツが配達されて、何とか意識を取り戻して受け取れた。開封とツイートは深夜に行った。

午後6時に目覚ましで起床。家を出て、相も変わらず込み合っているみどりの窓口に並び、帰省用に購入した新幹線の切符を払い戻した。日付変更もできるようだったが、まだ代わりの帰省の予定は決まっていない。

立ち食い蕎麦を食べてゲーセンへ。今のところ体調不良もないし、東京での行動は帰省を自粛するレベルではあったものの、ゲーセンすら行かず引きこもるほどではないという考えである。今日は14を頑張っていた。成果はまとめてツイートしたが、AJが三つ、SSS+が一つ。

J219は中盤の鍵盤がやたら上手かったため、ラストをどついてAJ通過することを祈るゲームになった。何回かやったら成功した。

AVALONはまっとうに頑張った。105、106小節は全部擦り。プレイを重ねるうちいたるところに癖がついて大変だった。

Elemental Creationはありえないくらいうまかった。今日2プレイ。105小節と107小節は擦って、それ以外は全部押した。ひたすら忙しくてあまり粘着したくない部類の譜面なので、しっかり決め切れてよかった。

今日1プレイ。ホールドの上に重なったタップで出した。次のプレイは序盤のタップスライドで出してしまい、やる気が一気に消え失せた。これも粘着したくない譜面。とりあえずSSS+に乗ったことは嬉しい。これで14全SSS+達成である。

ゲーセンにいる間に午後9時を迎え、AHC013が始まった。プレイの合間を縫って問題を読み、スマホから提出していた。

RECRUIT Nihonbashi Half Marathon 2022 Summer(AHC013) - AtCoder

ゲーセンが閉店した後、どこかで食事することもなくすぐ帰宅。それから次の日の昼までずっとネット小説を読んでいた。

まずなろう。午前7時に「限界超えの天賦《スキル》は、転生者にしか扱えない ー オーバーリミット・スキルホルダー」を読了。

https://ncode.syosetu.com/n5378gc/

非常に面白かった。以下の引用部分で言及していたものがこの作品である。

数日ほど睡眠時間を削って読みふけっていた日があったが、途中でそんなことをやっている暇はないということに気づいて、300話ほど読んだところで止まっている。

週記(2022/08/01-2022/08/07) - kotatsugameの日記

睡眠時間を削ってまで読みふけってしまったのは、途中で読むのをやめられなかったから。設定上の面白さとストーリー展開の面白さがどちらも好みだった。前者はつまり主人公のチート能力やその強さについてである。後者については、どことなく漂う不穏さが状況を否応なく先に進めていくのでグイグイ引き込まれた。

主人公の思い通りにいかない展開は主人公最強モノとはあまり両立しないように見えるし、自分でもストレス要因となってあまり好まないように思っていたが、しかしそういう展開が少し含まれていると面白さが増したと受け取るらしい。このことは「無職転生」や「ここではありふれた物語」など、自分に強い印象を残した作品に共通しているように感じられる。

今も書いたが、主人公最強モノ。個人的には2章が一番好きだった。まだあまり有名でない状態から強さを一気に見せつけるのは好みのシーンである。それが、見せつける対象を変えつつ何段階かで行われていた。

その後ハーメルンを読み、午後1時就寝。

08/10(水)

午後6時半起床、午後11時頃寝落ち、午前3時半起床、午前10時半頃寝落ち。その間はずっとハーメルンを読んでいた。

08/11(木)

午後7時起床。午後9時半にハーメルンを一つ読了。「非科学的な犯罪事件を解決するために必要なものはなんですか?」。

syosetu.org

これも非常に面白かった。普段はポンコツなのに決めるべきシーンではばっちり決める主人公が非常に格好いい。話が進むに従って主人公の能力や過去が徐々に明らかになっていき、それにつれて主人公が思ったより強いことが分かってくる。その分、最初数話は主人公が何者なのかわからず戸惑ったが、少し読み進めるとすぐ話に引き込まれた。まだ自分の想像を超えた設定があるようで、これからの展開が非常に楽しみ。

ちなみになろうのほうでも連載されている。そちらで一区切りついてから一気にハーメルンに投稿しているようなので、なろうのほうが新しい話を先に読める。

その後また別作品を読み始めたが、さすがに数日何もしていないのはまずいと思って布団から脱出し、午前3時頃から日記を書き始めた。午前9時半ごろ切り上げてまたハーメルンを読み始め、正午就寝。

08/12(金)

午後6時起床。布団でずっとラノベを読んでいた。

このままだと今日何もできずに終わってしまうなと危機感を覚え、午後9時20分から久しぶりにyukicoderのコンテストに出た。昨年の11月末以来だから、8か月ほど間が空いたらしい。

yukicoder contest 356 - yukicoder

Aはよい。Bは難しかった。最初に適当に待つことで、動き出したら取り除かれるまで止まらないと思ってよい。取り除かれるタイミングを1回目、3回目、5回目、……と固定して、後ろの駒から割り振っておく。そのタイミングで取り除かれるには何回目のタイミングから移動しなければならないか求め、もしこれが負だったら全体をその分ずらす。こうして求めた、先頭の駒が取り除かれるタイミングが答えになる。

CはAの具体的な値には興味がない。A_i\lt A_{i+1}なら+1、そうでなければ-1として累積和を取り、これを折れ線だと思うと、操作は山になっている部分を谷に折り返すことに相当し、操作をし切った状態が累積和の先頭(0)と末尾をつなぐ大きな谷として求まる。1回の操作である項だけが-2されるということから、和を比較することで操作回数が求まる。

Dは、ランレングス圧縮して実験すると、2段進んだとき両端から一つずつ要素を落とした形になることが見えた。より精密に確かめるため、並んだ3個のブロック23通りで2段進んだときを計算すると、ほとんどの場合は真ん中の要素が保存されることが分かった。しかし010の場合だけ0になってしまうらしい。よって最初の2段と、必要なら最後の1段だけを愚直に計算すればよい。

Eは手で試していると奇数桁目を全部1にしてよいことに気づいた。しかもそれを取り除いて偶数桁目だけ見ると、使える数字が2以上になった以外はほとんど同じ形の問題になっている。正確には隣り合う要素が異なる必要がないのだが、今N\le 500\lt 2^9なので気にしなくてよい。結論として、X_iiの2進数におけるtrailing zerosに1足した値となる。

Fは簡単。操作を逆順に考えることで、yが必ずNの約数となるとわかる。なので約数だけ取り出すことでyの値を持つdpができる。この制約なら最大で1344個なので、遷移は全部試してよい。aからbに遷移する際は、a\mid bかつa,2a,\dots,bCの要素が含まれないのが条件。aを決めるループの際にCからaの倍数であるようなものを抜き出しておくと、後者の判定が二分探索2回で行える。Nの約数のペア(a,b)であってa\mid bを満たすものは少なそうなので、十分高速。

Gは解けなかった。区間dpの方針で考えると、重複を省くのが難しくなる。かといって先頭から見ると、良い文字列であることがうまく判定できない。6完。

Cは\pm 1にした後転倒数になっていたらしい。面白い。Fで二分探索をしていた部分は、Cのうちaの倍数であって最小のものを見つけるだけでよかったようだ。正直何を書いても間に合うと思っていたので、判定の高速化は全く考えなかった。

またラノベ。午前0時に1冊読了。「クラスで2番目に可愛い女の子と友だちになった」2巻。1巻がかなり好みだった記憶があるが、2巻はそれほどでもなかったと思う。1巻の内容をあらかた忘れてしまっていたのも良くない。この巻はひたすら主人公とヒロインがいちゃつく巻で、加えて主人公の家庭の問題も表出する。甘ったるいシーンは強く印象に残らないし、家庭の問題の結末も正直納得できなかった。ただ決して悪くはない。悪くはないが、1巻が好みだった分の落差があったのも確か。

別のラノベを開いてまた読みふけっていたが、午前4時半からは日記を書き始めた。

書いているうちに、先週土曜日のTCO22 Wildcardの結果が公開されていたことに気づいた。ちゃんとシステスに通って3完のまま、19位に上がってレートは2969→2934(-35)。思ったより優しい。

https://community.topcoder.com/stat?c=round_overview&er=5&rd=19337

1000点問題をupsolveした。まず先週の解法から説明しよう。memberに含まれる人のみをbitDPして残りを単に人数で管理すると書いた。memberに含まれない人数をRとおく。bitDP部分がiからjに遷移する場合を考えてみる。ここでdp配列をdp_{i,\ast}dp'_{j,\ast}と書く。dp'は次のdp配列になる。

まず、ijのbitwise ANDが非ゼロの場合。このとき残りの人をどのように決めても必ず条件を満たすから、dp'_{j,l}には\binom{R}{l}\sum_k dp_{i,k}だけ足されることになる。(i,j)4^{M-R}-3^{M-R}通りあるので、愚直に書くとO((4^{M-R}-3^{M-R})\times R)となる。

次にそうでない場合。この場合残りの人が被るように遷移しないとならないため、k人からl人に遷移するときdp'_{j,l}にはdp_{i,k}\times\left(\binom{R}{l}-\binom{R-k}{l}\right)だけ足される。O(3^{M-R}R^2)となる。

これを高速化する。まず後者について、もはやiの情報は必要ないため、k人という情報ごとに和をとって最後にまとめて遷移してもよい。これでO(3^{M-R}R+2^{M-R}R^2)になる。次に前者だが、こちらはiどころかkの情報も必要ない。よってあらかじめdp_{\ast,\ast}の和をとっておき、そこからijのbitwise ANDがゼロの場合を取り除いたものを使えば十分である。O(3^{M-R}R+2^{M-R}R)となる。

以上で1年分の遷移がO(3^{M-R}R+2^{M-R}R^2)となり、これをY回繰り返しても余裕で通る。整理するとかなり簡単な高速化だった。コンテスト中は確か\sum_k dp_{i,k}をあらかじめ持ってどうにかしようとしていたはず。後者でiの情報が必要ないことに気づけなかったのだが、式を見れば明らかなので当時はかなり焦っていたのだろう。

午前8時前に布団に入った。すでに睡眠時間が6時間を切っているが、うっかりハーメルンを1作開き、話数が少なかったためそのまま最新話まで読んでしまった。「言語系チート授かったのでvtuber始めました」。

syosetu.org

特に好みではない。人に英語を教える描写など、喋れるのと教えられるのは全く違うはずとか余計なことばかり考えてしまった。異種族の言語も扱えるというのは設定として面白そうだったが、今のところこれはと思った展開はない。

午前9時半就寝。

08/13(土)

午後0時半の目覚ましで一瞬意識を取り戻したはず。その時に45分に目覚ましをかけなおしたが、そちらでは起きられなかったようだ。ふと気づくと55分だった。慌てて布団から跳ね起き、PCの前に座った。午後1時からMulti-Universities Camp 8回目。

"蔚来杯"2022牛客暑期多校训练营8_ACM/NOIP/NOI/CCPC/ICPC算法编程基础集训_牛客竞赛OJ

12問のうち半分がsolved数2以下というかなり難しいセットだった。そんな中チームでFDHIAJの6完、2位。かなりうまくいったらしい。自分はFHAを解いた。Fは唯一の簡単枠。Aはサンプルからエスパーすると再帰的な構造が見えるので丁寧に実装。

Hは自分の好きなタイプの問題だった。32bit符号なし整数を保持できるメモリがr[0]からr[2^{10}-1]まで2^{10}個あるマシンを使い、制限された操作だけで加・減・乗が入り混じった数式を評価せよというもの。ただし値は32bit符号なし整数で求める。プログラム領域とデータ領域が分かれておらず、r[0]をプログラムカウンタとしてr[r[0]\bmod 2^{10}]を実行しながら進んでいく。

実行する値を2^{10}進数で4桁の数だと思いabcdと表したとき、a=0,1,2,3に対応して4種類の操作が行える。a=0r[b]を出力して停止、a=1r[b]に入力を1文字読み込んで、読み込みに成功したらr[0]\leftarrow r[c]、失敗したらr[0]\leftarrow r[d]とする。この二つは入出力なので、計算として行える操作は実質残りの二つだけになる。a=2r[b]\leftarrow r[b]+r[c]r[0]\leftarrow r[d]a=3は条件分岐で、r[b]=0の場合r[0]\leftarrow r[c]、そうでない場合r[0]\leftarrow r[d]

数式の評価方法を丁寧に考えればすぐ解けた。実は変数を五つしか使わなくてよいため、実行する値をコード中で生成するとかアクロバットな手法は必要ない。

使う変数のうち二つは入力文字と一時変数。残りの三つをx,y,zとおく。加減算で分割した項のうち現在見ているもの以外をxにもち、見ている項の値をyzを使って計算する。数の掛け算はできないが、1文字読んでwとし、y\leftarrow 10y+wzと更新するのを繰り返すことで、yzと読んだ数を掛けた値を計算できる。+-が来た時点でx\leftarrow x+y,y\leftarrow 0,z\leftarrow\pm 1とし、*が来た場合はz\leftarrow y,y\leftarrow 0とする。初期状態はx=0,y=0,z=1である。

代入操作など存在しないので、いったんゼロクリアしてから足し算することになる。ゼロクリアは自分に自分を32回足すことで行えるが、わざわざ32回操作を書き並べなくともa=3の条件分岐を使ってwhileループを作れば問題ない。10y2\times y+2^3\times yとして計算する。そこにwzを足すのは愚直にループして行った。以上のアルゴリズムを丁寧に実装すればよい。操作がr[0]\leftarrow dではなくr[0]\leftarrow r[d]であることに注意。

今回はコードの最後のほうにチェッカーを書いた。普段はジャッジプログラムを別に作りがちなのだが、それは面倒なだけで利点は少ない。

しばらくTLでコンテストの感想を眺めたあと、食事し、午後7時から1時間半ほど仮眠を取った。起き抜けは明らかに頭が回っておらず大変だった。何とか眠気を振り払い、午後9時からABC264。

freee Programming Contest 2022(AtCoder Beginner Contest 264) - AtCoder

今日も全部C++。Aはよい。Bは読んですぐ書き始めたものの思ったより大変だった。まずR\ge Cとなるようにswapして下三角領域のみを考える。それをさらに二分割する必要があったためR+C\le 16をチェック。真ならばC\bmod 2、偽ならばR\bmod 2を見て、1の場合がblackに対応する。Cはbit全探索。Dは何も考えずにBFSした。

Eは逆から見る典型。UFで管理する集合に発電所が含まれるかのフラグを持たせたかったため、その場でUFをフルスクラッチしたら、なんとfind関数をかませ忘れるというミスを犯して1WA。どうやらまだ頭が寝ていたらしい。

Fはdpで、マス(i,j)にいるとき行iをflipしたか・列jをflipしたかのフラグを持ち、H\times W\times 4状態で計算する。例えば(i+1,j)に進むとき、A_{i,j}A_{i+1,j}の値が等しいかどうかで行i+1のflip状態を行iに合わせるかどうかが決まる。それ以外の行・列は関係しない。

Gは、文字列の末尾を状態、1文字追加するのを遷移だと思うと、最長経路問題になる。符号を反転して考え、ベルマンフォード法で負閉路の存在を判定。よく考えると状態としては末尾3文字ではなく2文字しか持たなくてよいため、先頭に埋めるためのダミー文字を含めても状態数27^2、遷移が状態ごとに26種類で十分高速。

Exは、頂点を追加するたびにその頂点を含むような完全二分木を数えることにする。まず深さd18以上の頂点は無視してよい。なぜなら、その頂点を含むような完全二分木は頂点数が2^{d+1}-1\ge 2^{19}-1\gt 3\times 10^5となるから。逆に、無視しなかった頂点の親はd個しかないため、それぞれ見て計算することが可能になる。具体的には、深さd'\lt dの親について「その頂点を根とし、深さがd-d'であるような完全二分木の個数」を考える。子について同様の値の和を管理しておけば、今追加した頂点による増分を持って遡っていくことで順次求まる。

Exが12分で解けたのは我ながらかなり速かった。50分足らずで全完。しかしEの1WAが響いて2位から3位に転落し、賞金額も25000円くらい減ったように見える。残念。

Bは\min(R,C,16-R,16-C)\bmod 2で判定できるが、コンテスト中の場合分けも丁寧に考えるとこの式と一致するようだ。Dは転倒数と聞いてびっくり。Eは発電所をひとまとめにするのがかなりうまいやり方に見える。ちゃんと思いついていればUFを空で書いてバグらせることもなかったかと思うと余計残念になってきた。

コードゴルフ。これはCF後に少し縮めたものも含む。A問題はVimで、全完後に22Bを書いたのだがコンテスト開始すぐにほぼ同じコードが提出されており負け。Bはdcで\max((R-8)^2,(C-8)^2)\bmod 2を判定。CはRubyAtransposecombinationを2回使うことで、インデックスを使わず直接行・列を抜き出している。

Dはatcoderの文字を順に消していくと、消すときのインデックスの総和が答えになる。転倒数を求めているのと同じ。これをPerlで書いた。実際にはatcodeの6文字だけでよいこと、またatcode=~/./gとするよりa,t,c,o,d,eと直接リストを書いたほうが短くなることが短縮ポイント。EFGは手つかず。Exはコンテスト中に書いたコードがそれなりにシンプルだったので縮めてみた。RubyだとTL 3secでかなりギリギリだったのに、Perlで書くと縮んだうえ速くなった。

午後11時半からCF #813 div.2。

Dashboard - Codeforces Round #813 (Div. 2) - Codeforces

Aは先頭k項のうちkより大であるものの数が答え。Bは未証明だが(n,n-1),(n-1,n),(n-2,n-3),(n-3,n-2),\dotsとペアにしていくのがよさそう。出したら通った。

Cはある地点を境に左は全部0、右は全部そのままとなる必要がある。そのような状態が可能な地点のうち最も左のものを探す。つまり、左右両方に出現する値が存在せず、また右側が単調増加になっているような地点。各数に対しそれが出現する最も左のインデックスを求めておいて、数列を右から順に見ていくことで、すべての地点に対してチェックできる。

Dは面倒だった。まずd(u,v)=\min(\min(a_u,a_{u+1},\dots,a_v),2\times\min a)である。よって最小値を大きくするように変更するのがよくて、そのもとで\max d(u,u+1)を計算すればよい。とりあえず小さいほうから10^9に変更して、選ぶ箇所に自由度がある値は特別扱いした後、\max\min(a_u,a_{u+1})を求めた。しかしWA。

最小値を少し小さくしてでも\min(a_u,a_{u+1})が大きいところを作ったほうが良いことがあると気づいて、各uについてまずa_u,a_{u+1}\leftarrow 10^9とした後最小値を押し上げるケースに対応した。しかしこれもWA。a_ua_{u+1}の両方ではなく片方だけ変更するようなコードも追加したら通った。疲労困憊。

Eは難しい。{\rm lcm}(i,j,k)\lt i+j+k\lt 3kなる(i,j,k)を数える。ここから{\rm lcm}(i,j,k)=k,2kが得られ、i,j\mid 2kが必要。さらに{\rm lcm}(i,j,k)=2kなら、つまりi\nmid kまたはj\nmid kならばi+j\gt kも必要。

E1はj=L\dots Rを固定した後j\mid 2kなるkを列挙して解いた。jをインクリメントする前にi=jとした場合の前計算をすることができる。まずi\mid kかどうかで分かれ、さらにi+j\gt k\Leftrightarrow k-i\lt jが必要かでも分かれるため、3種類の値を管理した。k-i\lt jという条件を扱うため、jを昇順に探索している。

E2はこれをL=1,R=2\times 10^5で解きつつクエリにオフラインで答える問題。i=R\dots Lと降順に見ることができれば、BITのk番目に答えを足しておいてi=Lとなった瞬間[L,R]区間和を取得することで解けるため、その降順に見る方法を考えなければならない。先ほどはk-i\lt jを満たした瞬間に前計算の配列をインクリメントしたが、逆にk-i\lt jを満たさなくなった瞬間デクリメントする手もあって、こちらだとiを降順に探索できる。

Fは解けず。システスは全部通ったがDでWAを出すし遅いしで順位は散々だった。その大変だったDは二分探索できるらしい。これだと細かいことを考えなくて済むのでかなり簡単。

しばらくYouTubeを見た後午前4時から日記を書き始めた。朝方切り上げて布団に入り、2時間ほどハーメルンを読んで、午前8時半就寝。

08/14(日)

午後7時起床。今日はAGC以外のコンテストがないが、かといって直前まで寝ているのはまずいため、眠気を振り払って身を起こした。10時間以上寝たのになぜまだ眠いのか。

食事したりシャワーを浴びたりとかなりゆっくり準備を整え、午後9時を待った。

コンテスト開始は10分延期された。午後9時10分からAGC058。

AtCoder Grand Contest 058 - AtCoder

Aから躓きそうになったが、天才的な閃きが降りてきて助かった。長さ4の順列(P_1,P_2,P_3,P_4)であってすでにP_1\lt P_2が満たされたものを考えると、高々1回のswapでP_1\lt P_2\gt P_3\lt P_4を満たせる。順列を全部試して確かめた。この事実を用いると、最初にP_1\lt P_2とした後、長さ4の連続部分列を2個ずつずらしながら揃えていくことで高々1+(N-1)回で条件を満たせる。これをどうやって思いつくかというのは難しい話で、やはり閃いたとしか言いようがない。一応、サンプル1を見て長さ4の順列を考えたのが初手だった。ともかく解けてよかった。

Bはほぼ既出。自分が見たのはyukicoderで、特に解説がかなり綺麗だったので丸ごと記憶に残っていた。念のため正当性を確かめた後、実装。すぐ通った。コンテスト後のTLで以下の問題だったことを知った。

No.1378 Flattening - yukicoder

Cは難しかったものの考察が順調に進み、かなり気持ちよく解けた問題。ただし考察の初手には飛躍がある。というのも、なんと1,4の頂点は全部葉であるとしてよいのだ。次数が2だとして、適当に繋ぎ変えることで1にできることを確かめた。例えば1頂点だけ1で他全部2の場合などはこの限りではないのだが、今4種類の頂点それぞれが必ず一つ以上存在するので、そのようなコーナーケースは考えなくてよい。よくわからない制約もちゃんと使えていて、答えに近づいていることを強く実感した。

1,4の頂点を全部無視したときに隣り合う2,3の頂点の各ペアについて、そこを結ぶパスの長さを考える。ペアの間にある1,4の頂点は、パスのどこかに接続するしかない。しかも交差してはならないため、十分な量の2,3の頂点を含む必要があって、パスの長さはある数以上とわかる。この値を、ちゃんと偶奇も確かめつつ求めておいて、ぴったりその長さのパスを作るように隣り合う二つの頂点集合をマージすることを繰り返した。

最初は長さ1のパスで結べるペアを結ぶ。するとその頂点集合は長さ1のパスを持つことになる。隣り合う二つの頂点集合が長さlrのパスを持ち、その間を結ぶためのパスの長さがmだとすると、新しく張る辺1本を含めてl+r+1\ge mの場合に結ぶことができて、マージした頂点集合には長さl+r-m+2のパスが残る。先ほど丁寧に偶奇を処理したのでここでは気にしなくてよい。すべての頂点集合をマージできるか、できたら最後に残ったパスがまだ結んでいないペアの要求を満たすかをチェック。

サンプルを合わせて出したら通った。CのACは4番目で、全体5位。Dに2時間残したことだしこれはレート爆上げか!?と思ったものの、以降はずっと椅子を温めることになった。

Dは、とりあえず文字を012に直した後インデックス\bmod 3でずらしてから考察を開始した。包除原理を疑いつついろいろ見るうちに、直接求められる気になって、そこから戻ってこられなかった。ABCABBCCAの6種類に分けると、どれの後にどれが来てよいかというグラフが書け、それだけで可能な文字列を表せる。なのでそのグラフとずっとにらめっこしていた。個数の条件を扱えなかったので、ここで包除原理を使いどうにか制約を緩めた問題に帰着させようとしたものの、どうにもならなかった。

3完最速で42位、パフォーマンス3098、レートは2835→2864(+29)。highestを更新し、銅冠を戴いた。Dに苦しみすぎており実感は薄い。

Cは、わざわざマージ条件をチェックしなくともとにかく結びまくって、最後に残ったパスの長さが正か見ればよかったらしい。これならもうちょっと速くACできただろうが、3完最速である以上速度面の改善は全く関係ない。D問題はコンテスト中の考察だとあと何時間あっても答えにはたどり着けない。

TLで感想戦を眺めたり、Dの解説を読んだりしていたら、コンテスト終了からかなり時間が経っていた。眠気が強いので日記も書かず午前4時に布団に入った。

昨晩の続きのハーメルンをしばらく読んでいたが、どうにも読みづらかったので放棄した。誰がどういう立場のキャラなのか、味方か敵なのかがわからない。原作を知らないし、本文にも特に描写されないのだ。ぬらりひょんの孫はアニメをたまに見たという記憶があるだけ。呪術廻戦に至ってはTLに流れてくるコラ画像が精一杯。

syosetu.org

別のハーメルンを開いて、こちらは非常に面白かったので一気に読み終えた。「ローカル役でしかあがれない」。主人公の能力が非常に豪快でよい。配牌が純正九蓮宝燈の人と、それと同じ種類の牌しか持たない人がいた場合、後者は何を切っても前者の役満に放銃することになる。当たり前だが実際に描かれると感動。

syosetu.org

午前8時就寝。

日記を書いていないのに4時間もハーメルンを読んでいたらしい。結局先週末のDDCC参加記もまだ書けていないし、大変。書く気はあると明言しておきたい。