[AGC026] B - rng_10s

[Q] https://atcoder.jp/contests/agc026/tasks/agc026_b

循環するからなんかのmodをとると思うと、とても嫌な気持ちになるけど、この問題は大丈夫なやつ。
知識がない状態でも、ループを見つけて法則を探れば分かる。

1. とりあえず、A<Bなら最初でNo
2. とりあえず、B>Dなら、いつか追い込まれるのでNo
3. シミュレートして法則を見つける
4. 収入Dと支出Bだけ変動するので、gcd(B, D)の変動を網羅することになるだろうと思う。
5. あと1回Bしたら、C以下になるような在庫数xを考える。
6. ありうるxの最小値がBに満たないならNo

Q. シミュレート?

・ex1
9 7 5 9
9 →昼 2 →夜 11 →昼 4 →夜 13 →昼 6 →夜 6 →昼 x

4. 永遠に回すので、収入と支出の最大公約数だけループするのがわかる。
ll g = gcd(B,D) = gcd(7,9) = 1

5. あと1回とったらC以下になる在庫数を抽出すると、
9,11,6,xとなっているようだ。
        ~
gcd が 1なので、あと1回とったらC以下になる在庫数として取りうる値は、
6,7,8,9,10,11,12
の7種類が全部出るんだろうと思う。
low = 6

6. そして、low = 6 < 7(B) なので、Bを満たせずNo


・ex2
9 7 6 9
9 →昼 2 →夜 11 →昼 4 →夜 13 →昼 6 →夜 15 →昼 8 →夜 8 →昼 1 →夜 10 →昼 3 →夜 12 →昼 5 →夜 14 →昼 7 →夜 7 →昼 0 →夜 9

gcd = 1
5. あと1回とったらC以下になる在庫数は、
9,11,13,8,10,12,7
つまり
7,8,9,10,11,12,13
これはgcd=1だから網羅する。

low = 7 >= Bなので、Yes。

実装

bool solve() {
 ll A, B, C, D;
 cin >> A >> B >> C >> D;
 if (A < B) return false;
 if (B > D) return false;
 ll g = gcd(B, D);

 ll high = A % B;
 // highは±gを任意に変動しうる。
 // high < C である限りgを足す
 high += ((C - high + g - 1) / g) * g;
 // ぴったりならもう1回
 if (high <= C) high += g;
 if (high < B) return false;
 return true;
}

int main() {
 cincout();

 ll T;
 cin >> T;
 rep(i, T) {
   if (solve()) {
     cout << "Yes" << endl;
   } else
     cout << "No" << endl;
 }
}

https://atcoder.jp/contests/agc026/submissions/31098530

いいなと思ったら応援しよう!