AtCoder Grand Contest 026 - B rng_10s
解説を軽く読んだだけでは理解できない脳なので記事書いた.
問題
https://beta.atcoder.jp/contests/agc026/tasks/agc026_b
- 昼:客のsnukeさんがジュースをB個買う
- 夜:rngさんが在庫を確認し,C個以下なら翌日朝までにジュースをD個仕入れる
ある日の朝,ジュースの在庫はA個だった.snukeさんは永遠にジュースを買い続けられるか?
考察
1
いくつかのケースは,簡単な場合分けで除外できる.
つまり, が仮定できる.
2.1
残りのケースについて考察するために,愚直コードを書く.
using ll = long long; bool solve(ll A, ll B, ll C, ll D) { ll zaiko = A; set<ll> memo; while(true) { if (memo.count(zaiko)) return true; memo.insert(zaiko); // snuke if (zaiko < B) return false; zaiko -= B; // rng if (zaiko <= C) zaiko += D; } }
2.2
zaiko -= B;
をループで回す事に良い気がしないので,これを改善したい.
結論から書くと,zaiko = zaiko % B;
と書き直せる.C<=B
なので,補充せずともまだもう1度買えるのに,補充されてしまう事態が起きない.よって,Bの倍数個買えるだけ買ってしまっても同じ.言葉をそのままコードで書くとzaiko = zaiko - zaiko/B*B;
だが,これは剰余なので,zaiko = zaiko % B;
.
改良後のコードは次.しかし計算量オーダは改善されていない.
bool solve(ll A, ll B, ll C, ll D) { if (B > A) return false; if (B > D) return false; if (C >=B) return true; ll zaiko = A % B; set<ll> memo; while(true) { if (memo.count(zaiko)) return true; memo.insert(zaiko); if (zaiko > C) return false; zaiko = (zaiko + D) % B; } }
3.1
改善後のコードを別の表現に書き直す.
が与えられた時,i日目(0-indexed)の在庫の数 を表現する.
もし, となるような が存在するならNO,存在しないならばYESが言える.
3.2
リファクタリングする.
と書き直せる.
また,全ての の中で最も大きな値を と置く. さえ分かれば,この1つの値のみを使って比較すれば良く, ならばNO, ならYESが言える.
4
はどうやって探せばいいの?
http://codeforces.com/blog/entry/60562?#comment-445305 の解説が詳しい. (以降の解説はこのURLの内容と殆ど同じ・大きく依存します…)
- 補題: は 個の異なる値が存在して,その間隔は になっている.
補題から,F_iとしてあり得る値は,kを整数として, である.
最大値について知りたいので, が最大となるkを探したい.
式変形を行うと, が得られる.
極大のkが見つかれば良いので,
よって, は,.
この値とCを比較してYESNOを出力する.
補題の証明
todo: (素数とか考えるとなんとなく真っぽい)