buyoh.hateblo.jp

残念な競技プログラミング参加者による学習記録

AtCoder Grand Contest 026 - B rng_10s

解説を軽く読んだだけでは理解できない脳なので記事書いた.

問題

https://beta.atcoder.jp/contests/agc026/tasks/agc026_b

  • 昼:客のsnukeさんがジュースをB個買う
  • 夜:rngさんが在庫を確認し,C個以下なら翌日朝までにジュースをD個仕入れる

ある日の朝,ジュースの在庫はA個だった.snukeさんは永遠にジュースを買い続けられるか?

考察

1

いくつかのケースは,簡単な場合分けで除外できる.

  • B > A.1日目でreject
  • B > D.購入する速度が仕入れる速度を上回るため,inf日目には必ずreject
  • B <= C.在庫を枯渇させる前に必ず仕入れがある.

つまり,B\le A,B\le D,C\lt B が仮定できる.

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

改善後のコードを別の表現に書き直す.

A,B,C,D,B\le A,B\le D,C\lt B が与えられた時,i日目(0-indexed)の在庫の数 F_i を表現する.

  •  F_0 = A \bmod B
  •  F_{i+1} = (F_i+D) \bmod B

もし,  F_i > C となるような i が存在するならNO,存在しないならばYESが言える.

3.2

リファクタリングする.

F_i = (A + D\times i) \bmod B と書き直せる.

また,全ての F_i の中で最も大きな値を F_\max と置く. F_\max さえ分かれば,この1つの値のみを使って比較すれば良く, F_\max > C ならばNO, F_\max \le C ならYESが言える.

4

F_\max はどうやって探せばいいの?

http://codeforces.com/blog/entry/60562?#comment-445305 の解説が詳しい. (以降の解説はこのURLの内容と殆ど同じ・大きく依存します…)

  • 補題 (A+iD)\bmod BB/\gcd(B,D) 個の異なる値が存在して,その間隔は\gcd(B,D) になっている.

補題から,F_iとしてあり得る値は,kを整数として, 0\le (A\bmod B)+k\gcd(B,D) \lt B である.

最大値について知りたいので, (A\bmod B)+k\gcd(B,D) \lt B が最大となるkを探したい.

式変形を行うと, k \lt \frac{B-(A\bmod B)}{\gcd(B,D)} が得られる.

極大のkが見つかれば良いので, k = \lceil\frac{B-(A\bmod B)}{\gcd(B,D)}\rceil -1

よって,F_\max は, (A+ (\lceil\frac{B-(A\bmod B)}{\gcd(B,D)}\rceil -1)\gcd(B,D) \bmod B)

この値とCを比較してYESNOを出力する.

補題の証明

todo: (素数とか考えるとなんとなく真っぽい)