ABC169

1ペナ全完。これでこの順位はしょっぱいなあ。一応レーティングは上がった。

Bみたいのどんどん出題されてほしいけど、かなり難しいと思って時間をかけたのにRE食らってめちゃくちゃ残念。Eは順位表にペナが少ないのを見て甘えた。Fの解法は天才感あった(これをあんな大量に通してるのおかしいでしょ)。

A - Multiplication 1

「積雪深差」を思わせる簡単なA。用意したテンプレにa*bと書き込むだけだったので、少し見直しもしたのに16秒。これは早い。Aはこのくらい簡単でいいと思う。

B - Multiplication 2

俺はプロだから見た瞬間わかるんだけど、これはめちゃんこ難しい。10^17*10^17みたいな掛け算が生じうるから、掛けてからの大小判定ができない。10^18を途中で超えても0を掛けて0にされちゃうことに気づいたときはあまりの難しさに笑ってしまった。まず、ll(long long)で10^18の定数を用意する。1e+18(double)から変換しても、さすがにこのキリのいい数はdoubleで厳密に表せる(constexprにすればIDE上で確認できる)。入力を受け取りつつ、0が来たら0を出力して終了。10^18を超えるかの判定は、A*R>10^18 をしたいので、A>M/R、10^18を少し超えたものを見逃すのはいいけど10^18以下の数を超えたと誤判定するのはダメなので、切り上げてA>(M+R-1)/R。掛け算の前後に超えたかの判定をして、超えてたら最終的に-1を出力する。

提出してRE。難しいのはわかっていたのでかなり慎重にやった。しかし頭がオーバーヒート気味で、バグを残してしまった。最近どこかでこういう処理の周辺をかなり調べたのになあ。このコードでREの原因は0除算くらいしかない。既に超えたと判定していたら割り算はしないように修正、AC。(オーバーフローすると乗算の結果が0になることがある(2^32*2^32など))

REのとき表示がいつもと違うことに気づいたが、しばらくするとClarに「ジャッジ結果の表示方法が変更されています」というお知らせが来た。

C - Multiplication 3

これは簡単。しかし楽な実装を考えるのは難しい。特に思いつかないので愚直な実装で。Bを文字列として受け取り、小数点を無視して整数化。最初、答えが最大で10^19近くなると勘違いして、unsigned long longで1.6*10^19まで行けることを確認していた。しかしよく見ると10^18未満なので何も考えなくてよかった。値を受け取ったら掛けて100で割ればよい。

D - Div Game

要するに、素数冪で割っていく。素因数分解して、各素数が何個あるかわかればよさそう。速度も要求されないので、std::mapを使って数えた(楽だった)(約数列挙と違って反対側を見なくていい)。同じ数は使えないので、p^1, p^2, p^3 のように取れるだけ取ったら、それより多くは取れないし半端は一番大きいやつに押し付ければ達成できるので、その個数が最大。普通の問題もいいものだ。

(解説配信を見て)あ、Nを1にしなくていいの忘れてた。

E - Count Median

何でソートするのかなーとか。図を描いて右にN/2個とか考えても全然わからんしFと交互に考えていた。EFを見て両方解ける可能性がそれなりに高いと思ったので、双方に惜しみなく時間を使う。

まあ中央値の最大値を考える。すると、Bを(Aは無視で)ソートして真ん中のものがそうだ(Nは奇数とする)。中央値以上のものがN/2(切り捨て)個ないといけないので。たまにあるパターンではあるけど、Aが関係ないのやばいね。同様に最小も求まり、その間の数は全部達成できるのでは?いくつかのあやしそうな例で考えてもそうだし、順位表を見てみんな解いててペナが少ないし、もう時間もかなり来てるので、提出してAC。これは難しい。想像も証明もできない。

F - Knapsack for All Subsets

ABC159の"Knapsack for All Segments"を思わせる。空集合もありにしていいことを確認。なんか高速ゼータ変換が浮かぶけど、Nが3000になるので2^N個はハチャメチャに多い。とりあえず普通のDPを書いてみたり。どうも2^Nの大きさを忘れがちで思考がグルグルしていた。さて、f(全体)は求まったがこれからどうするか。一応、ちょうどi個で和がSになるやつらの通り数が各iについてわかれば解ける。O(N^3)のDPでできそうなのでそれを高速化したい。けっこう悩んだ。"Knapsack for All Segments"のときは、普通のDPとほぼ同じコードで解けたので、その方面で考えていると何かひらめく。ばらばらに(各iについて)求める必要はない。ちょうどi個のやつらは2^(N-i)回足されるので、DPのタネを最初に2^N倍しておいて、DPの更新で足すたびに「お前らは半分ね」していけば。ちょっとコードがバグってたけど落ち着いて直してサンプルが合い提出してAC。これは天才みがあって気持ちのいい問題だ。