先日コンテスト中に緑diffの問題が解けなくて悔しかったので、久々にブログ書いてみます!
どういう思考をたどれば解けた可能性があるのか振り返っていきます。
問題概要
問題のリンク
* 正整数 が与えられる。
* 以上
以下の整数
を選んだとき、
の積が
以上になるようなものの中で最小の値を求める。
* そのような値が存在しなければ を出力する。
制約
結構でかいので工夫が要りそうですね。
計算量を や
ぐらいに抑えたい感じの印象です。
考察
視覚的なイメージ
の積を扱うので、横
, 縦
の長方形を考えると視覚的にとらえやすい気がしました

で、これがどうなってると良いかというと、面積が以上になれば良いので、下図の青いらへんのゾーンに
を選べば良いです!

がボーダーなんですけど、
を変形すると
になって、反比例の式
と形が一緒ですね。
aを固定する
で、図を描いてみたは良いが、これだけだとまだどこから手を付けて良いか分からなさがありますね
通り探索するのは範囲が多すぎる!
こういうとき片方を固定すると良いことがあるので を固定して考えてみます
の値が決まってれば、
を満たす最小の
だけ考えればよくなって、これって計算で求められそう!
実際に、そのような は
(天井関数) で求められます!
計算量をどうする?
を固定すれば 最適な
が求まることがわかったところで、まだもう1ステップ考えることがあって、計算量の壁です!
このままだとまだ を
通り探索いなきゃいけない気持ちになりますよね。
どうしましょう!
(ここ普通に難しいと思った)
実は と
は対等な関係なので、
と仮定してよいです! (
としても一般性を失わない みたいな表現をされるやつ) です!
となったら探索を打ち切って良いのです!
が大体
なので、この仮定をいれることによって計算量が大体
になるはずです。
ACコード
こちらはコンテスト後に通ったコードです。
N,M = map(int,input().split()) INF = float('inf') ans = INF for a in range(1,N+1): b = -(-M//a) if b > N: continue if b < a: break ans = min(ans, a*b) print(-1 if ans==INF else ans)
6行目の -(-M//a) はpythonで使える切り上げテクです! (M を a で割ったあまりを整数に切り上げています)
とっさに使えると強いです!
8行目の break はTLEしないために大事です。
なぜ解けなかった?
まともな考察が踏めてませんでした あたまがごりちゃん
の値を決めて約数を列挙するみたいな発想しか浮かばなくて沼ってました
間違った方針に行くとかなり沼るやつでしたね。。
こういうのを安定して解けるようになりたい。
おわり
冷えたけど、学びの多い良問でした!
青コーダーだけど ABC-D にも学びがある!