あーーーーーー、これは大好物系なん!!!!!
本番でやってみたかったん!!!!!
問題概要 (AGC 026 B)
整数 があたえられる。
最初は在庫に 個あって、
- 毎日 個ずつ消費される
- その日の最後に 個以下しか残っていなかったら 個補充される
というのを繰り返す時、永遠に回せるかどうかを判定せよ (というクエリが 回与えられる)。
制約
考えたこと
とりあえず色んな状況があり得るのでちゃんと整理しないと...な感じなん。
- < のときは自明に "No" (初期状態ですでにダメ)
- < のときはジリ損するので "No" ( で毎ターン回復するとしてもいずれダメになる)
というのがすぐにわかり、以後 , としてよい。これで少し考えやすくなる。このとき、
のような感じになる。
- のときは黄色い部分が十分広くて受け止めてくれるので "Yes"
というのがまずは見て取れる。そしてこの構図を眺めていると、初期状態 からスタートして、結局は を足したり を引いたりしていることがわかる。そこで問題は
を 以上の整数として と表されるような整数の中に、上図の黄色いところをちょうど綺麗に飛び越えてしまうような値 (具体的には より大きくて 未満の数) が含まれていないか否か
を判定するような問題になる (厳密にはちゃんと示さないといけない)。
そして拡張 Euclid の互除法からの着想から、 で表される整数とはすなわち として、
と表されるような整数を動いていくことがわかる。したがって、 として表される数の中に、
- より大きくて 未満の数が含まれていない: "Yes"
- より大きくて 未満の数が含まれている: "No"
とすればよい。ところで、「」の の倍数の部分は負も含むことに注意。これは「 で割って % あまる数」と考えても同じになる。
#include <iostream> using namespace std; long long a, b, c, d; long long GCD(long long a, long long b) { if (b == 0) return a; else return GCD(b, a % b); } bool solve() { if (a < b) return false; if (d < b) return false; if (c >= b-1) return true; long long g = GCD(b, d); // g で割って a%g あまる数で、c より大きく b 未満な整数があるかどうか // g で割って a%g あまる数で、b 未満の最大の数 long long Max = (b - a%g + g-1)/g*g + a%g - g; // b が g の倍数であることを考慮して、Max = b + a%g - g でも OK if (Max > c) return false; else return true; } int main() { int T; cin >> T; for (int CASE = 0; CASE < T; ++CASE) { cin >> a >> b >> c >> d; if (solve()) cout << "Yes" << endl; else cout << "No" << endl; } }