传送门
题意简述:多组询问,每次给出
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d,求满足
a
b
<
p
q
<
c
d
\frac ab<\frac pq<\frac cd
ba<qp<dc的所有二元组
(
p
,
q
)
(p,q)
(p,q)中
q
q
q为第一关键字,
p
p
p为第二关键字排出来字典序最小的那一对。
思路:
设计函数
f
(
a
,
b
,
p
,
q
,
c
,
d
)
f(a,b,p,q,c,d)
f(a,b,p,q,c,d)。
按照题目中保证
q
q
q最小的要求考虑该函数几个边界:
- ⌊ a b ⌋ + 1 ≤ ⌈ c d ⌉ − 1 \left\lfloor\frac ab\right\rfloor+1\le\left\lceil\frac cd\right\rceil-1 ⌊ba⌋+1≤⌈dc⌉−1,这个时候 p = ⌊ a b ⌋ + 1 , q = 1 p=\left\lfloor\frac ab\right\rfloor+1,q=1 p=⌊ba⌋+1,q=1时字典序最小。
- a = 0 a=0 a=0,这个时候 0 < p q < c d ⇒ q > d p c 0<\frac pq<\frac cd\Rightarrow q>\frac{dp}c 0<qp<dc⇒q>cdp,显然 p = 1 , q = ⌊ c d ⌋ + 1 p=1,q=\left\lfloor\frac cd\right\rfloor+1 p=1,q=⌊dc⌋+1的时候字典序最小。
然后考虑如何辗转缩小问题规模:
-
a
>
b
a>b
a>b
o
r
or
or
c
>
d
:
原
式
⇔
a
%
b
b
<
p
q
−
⌊
a
b
⌋
<
c
d
−
⌊
a
b
⌋
c>d:原式\Leftrightarrow\frac{a\%b}b<\frac pq-\left\lfloor\frac ab\right\rfloor<\frac cd-\left\lfloor\frac ab\right\rfloor
c>d:原式⇔ba%b<qp−⌊ba⌋<dc−⌊ba⌋
⇒ f ( a , b , p , q , c , d ) = f ( a % b , b , p , q , c − ⌊ a b ⌋ d , d ) , p + = ⌊ a b ⌋ q \Rightarrow f(a,b,p,q,c,d)=f(a\%b,b,p,q,c-\left\lfloor\frac ab\right\rfloor d,d),p+=\left\lfloor\frac ab\right\rfloor q ⇒f(a,b,p,q,c,d)=f(a%b,b,p,q,c−⌊ba⌋d,d),p+=⌊ba⌋q -
a
≤
b
a\le b
a≤b
a
n
d
and
and
c
≤
d
:
原
式
⇔
d
c
<
q
p
<
b
a
c\le d:原式\Leftrightarrow \frac dc<\frac qp<\frac ba
c≤d:原式⇔cd<pq<ab
⇒ f ( a , b , p , q , c , d ) = f ( d , c , q , p , b , a ) \Rightarrow f(a,b,p,q,c,d)=f(d,c,q,p,b,a) ⇒f(a,b,p,q,c,d)=f(d,c,q,p,b,a)这样会回到第一步。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
ll a,b,c,d,p,q;
inline ll gcd(ll a,ll b){while(b){ll t=a;a=b,b=t-t/a*a;}return a;}
inline void calc(ll&a,ll&b){ll d=gcd(a,b);a/=d,b/=d;}
inline void solve(ll a,ll b,ll&p,ll&q,ll c,ll d){
calc(a,b),calc(c,d);
if(!a){p=1,q=d/c+1;return;}
ll x=a/b+1,y=c/d+(c%d>0)-1;
if(x<=y){p=x,q=1;return;}
if(a<=b&&c<=d)return solve(d,c,q,p,b,a);
solve(a-a/b*b,b,p,q,c-d*(a/b),d),p+=q*(a/b);
}
int main(){
while(~scanf("%lld%lld%lld%lld",&a,&b,&c,&d))solve(a,b,p,q,c,d),printf("%lld/%lld\n",p,q);
return 0;
}