yukicoder No.321 (P,Q)-サンタと街の子供たち
すごく好きなタイプの問題です。
解法
a*P + b*Q は, P と Q の最大公約数 G に関して, c*G (c は整数) という整数値すべてを取ることはよく知られています。
今回も似たようなことが出来ますが, (c*G, d*G) (c, d は整数)を満たすすべての座標にいける, と言った単純なことは成り立ちません(一番最後の入力で間違ってることに気づく)。
なのでもうちょっと冷静に問題を見てみます。よく考えると, たどり着ける座標 x, y は,
x = a*P + b*Q
y = c*P + d*Q
と書けますが, この a, b, c, d の間に何らかの条件が成り立ちます。具体的には, a と d の偶奇が一致, b と c の偶奇が一致しなければなりません。x 座標で P 動かすと y 座標で Q 動かさないといけない, x 座標で Q 動かすと y 座標で P 動かさないといけない, という関係からこの偶奇性が出てきます。
ということで, a, b, c, d の値を求めましょう。拡張ユークリッドの互除法を用いて, G = x*X + y*Y であるとわかったとすると, 目標地点にたどり着くための a, b および c, d がひとつずつ求まります。
ただ, 例えば a, b の答えの組はこれだけではなく, a = a+Q/G, b = b-P/G としたものも, x 座標に関して座標を合わせる答えの一つになります。こんな感じで a, b, c, d の偶奇を調整して, a と d の偶奇が一致しかつ b と c の偶奇が一致するようなものがあれば良いわけです。
また, さきほど a, b の答えの組は一つではないと言いましたが, 実際には調べなければならない組は, 拡張ユークリッドの互除法を使って求めたものと, その答えを a = a+Q/G, b = b-P/G と調整したものの 2 組だけです。これは, 2 回以上 Q/G とかを使って調整しても, また偶奇が戻ってしまうことからわかります。
ということで, 各頂点に対して O(1) で到達判定ができるので AC 出来ます。
// extgcd ll extgcd(ll a, ll b, ll& x, ll& y) { if (a < b) swap(a, b); ll d = a; if (b != 0) { d = extgcd(b, a % b, y, x); y -= (a / b) * x; } else { x = 1; y = 0; } return d; } int main() { cin.tie(0); ios::sync_with_stdio(false); ll P, Q, X, Y; cin >> P >> Q; ll G = extgcd(P, Q, X, Y); int N; cin >> N; int ans = 0; for (int i = 0; i < N; i++) { ll x, y; cin >> x >> y; if (G == 0) { if (x==0 && y==0) ans++; continue; } if (x%G!=0 || y%G!=0) continue; ll a = (x/G)*X, b = (x/G)*Y; ll c = (y/G)*X, d = (y/G)*Y; bool ok = false; for (int j = 0; j < 2; j++) for (int k = 0; k < 2; k++) { ll na = a+j*(Q/G), nb = b-j*(P/G); ll nc = c+k*(Q/G), nd = d-k*(P/G); na = abs(na%2), nb = abs(nb%2); nc = abs(nc%2), nd = abs(nd%2); ok |= (na==nd && nb==nc); } if (ok) ans++; } cout << ans << endl; return 0; }