【BZOJ2712】[Violet 2] 棒球(类欧几里得算法)
大致题意: 给定分数\(\frac pq\)四舍五入到第\(n\)位的值,求\(q\)的最小值。
前言
可以先去看看【BZOJ2187】fraction,和这题几乎完全一样。
然而在细节上却又有些不同,导致我\(WA\)了一发又一发。。。
类欧几里得算法
首先考虑我们把题目中给定的小数转化为分数,即求出\(a,b,c,d\)满足\(\frac ab\le\frac pq<\frac cd\)。
然后具体做法不说了,上面那题都有。
唯一要注意的是,这题中有一侧是可以取等的,且我们取倒数的时候取等的方向会改变。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define LL long long
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n;LL v,tn;
I void read(LL& x)
{
char c;W((c=getchar())^'.');x=0;W(isdigit(c=getchar())) x=(x<<3)+(x<<1)+(c&15);
}
I LL gcd(Con LL& x,Con LL& y) {return y?gcd(y,x%y):x;}
I void Get(Con LL& a,Con LL& b,Con LL& c,Con LL& d,LL& x,LL& y,CI op)//类欧几里得算法,op表示取等方向
{
LL Mn=op?a/b+1:(a-1)/b+1,Mx=op?c/d:(c-1)/d;if(Mn<=Mx) return (void)(x=Mn,y=1);//根据op决定能取的整数的上下界
if(!a) return (void)(x=1,y=d/c+1);//a=0
a<b?(Get(d,c,b,a,x,y,op^1),swap(x,y)):(Get(a%b,b,c-(a/b)*d,d,x,y,op),x+=y*(a/b));//a<b;a≥b
}
int main()
{
RI i;LL a,b,c,d,g,x,y;W(~scanf("%d",&n))
{
for(read(v),tn=i=1;i<=n+1;++i) tn*=10;//读入小数部分,同时计算10的幂
g=gcd(a=max(v*10-5,0LL),b=tn),a/=g,b/=g,g=gcd(c=v*10+5,d=tn),c/=g,d/=g;//转化为分数,注意下界不能为负
Get(a,b,c,d,x,y,0),printf("%lld\n",y);//求答案,输出分母
}return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒