把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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;
}
posted @ 2020-06-03 19:13  TheLostWeak  阅读(207)  评论(0编辑  收藏  举报