G - A/B < p/q < C/D Editorial
by
sounansya
まず、条件を満たす最小の \(q\) を取った時に、 \(q\neq 1\) なら対応する \(p\) は一つに定まります。
簡単のために $\displaystyle \frac{A}{B} < \frac CD \le 1$ の場合を考えます。 条件を満たす最小の $q>1$ に対し $\displaystyle \frac pq,\ \frac{p+1}q$ が両方条件を満たすと仮定すると、 $\displaystyle \frac pq < \frac p{q-1} <\frac{p+1}q$ が成立するので $q$ が $q-1$ の場合も条件を満たすことから背理法より従います。証明
よって、 \(q\) を求めるのではなく最小の \(q\) を持つ \((p,q)\) の組を求めることを考えます。ただし、便宜上 \(q=1\) の時は \(p\) が最小になるように取ることにします。この問題を \(\displaystyle f\left( \frac AB,\frac CD\right) = (p,q)\) と表記します。
ここで、 \(\displaystyle f\left( \frac AB,\frac CD\right)\) は以下のアルゴリズムで再帰的に求めることができます。
- \(\displaystyle n=\left\lfloor \frac AB\right\rfloor\) とする。 \(\displaystyle (p,q) = f\left(\frac AB - n, \frac CD - n\right) \) に対し \(\displaystyle f\left( \frac AB,\frac CD\right)=(p+nq,q)\) となるので、これにより \(\displaystyle \frac AB<1\) の場合に帰着させることができる。
- もし \(\displaystyle \frac CD > 1\) なら \(q=1\) が条件を満たすので \(\displaystyle f\left( \frac AB,\frac CD\right)=(1,1)\) となる。
- \(\displaystyle \frac CD\le 1\) の場合、 \(\displaystyle (p,q)= f\left( \frac DC,\frac BA\right)\) として \(\displaystyle f\left( \frac AB,\frac CD\right)=(q,p)\) が成り立つことを使い小さいケースに帰着させる。
有理数の連分数展開を考えることでこれらのステップは \(O(\log \max\lbrace A,B,C,D\rbrace)\) ステップで終了することが分かります。
以上を適切に実装することでこの問題を解くことができます。計算量は各テストケース毎に \(O(\log \max\lbrace A,B,C,D\rbrace)\) です。
import sys
input = sys.stdin.readline
def solve(a, b, c, d):
n = a // b
a -= n * b
c -= n * d
if c > d:
return n + 1, 1
p, q = solve(d, c, b, a)
return p * n + q, p
for _ in range(int(input())):
a, b, c, d = map(int, input().split())
p, q = solve(a, b, c, d)
print(q)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
pair<ll, ll> solve(ll a, ll b, ll c, ll d) {
ll n = a / b;
a -= n * b, c -= n * d;
if (c > d)
return {n + 1, 1};
auto [p, q] = solve(d, c, b, a);
return {p * n + q, p};
}
int main() {
cin.tie(nullptr);
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--) {
ll a, b, c, d;
cin >> a >> b >> c >> d;
auto [p, q] = solve(a, b, c, d);
cout << q << '\n';
}
}
posted:
last update: