【XSY3917】分数(思维,构造)

题面

分数

题解

手推一下发现最优策略是:(不会证)

  1. \(x<0\),则不断 \(x\gets x+1\) 直到 \(x\geq 0\) 为止。

  2. \(x>0\),则 \(x\gets -\dfrac{1}{x}\)

  3. \(x=0\),终止。

也就是说,设开始时 \(x=\dfrac{a}{b}<0\)(若 \(x>0\) 则执行 \(2\) 操作),其中 \(a<0\)\(b>0\)

\((a,b)\) 表示 \(\dfrac{a}{b}\),那么上述操作本质是不断将 \((a,b)\xrightarrow{\text{若干次操作1}} (b-(-a)\bmod b,b)\xrightarrow{\text{操作2}} (-b,b-(-a)\bmod b)\)

为了方便,用 \(\xrightarrow{k}\) 表示 \(\xrightarrow{\text{k次操作1}}\),用 \(\Rightarrow\) 表示 \(\xrightarrow{\text{操作2}}\)

看上去很像辗转相除法,但时间复杂度是不对的,举个反例很容易证明: \((-1,114514)\xrightarrow1 (114513,114514)\Rightarrow(-114514,114513)\xrightarrow1(-1,114513)\)

所以暴力模拟是 \(O(V)\) 的(其中 \(V\)\(a,b\) 值域)。

但有一种神仙优化方法:

设当前 \((-a,b)\),考虑在 \(0<b<a<2b\) 时,显然要进行的操作是:

\[(-a,b)\xrightarrow{2} (-a+2b,b) \Rightarrow (-b,-a+2b) \]

发现 \(a'=b=a-(a-b)\)\(b'=-a+2b=b-(a-b)\),所以新的 \(a'\)\(b'\) 其实是在 \(a\)\(b\) 的基础上各减去了 \(a-b\),而且 \(a'-b'=a-b\) 不变。

那么如果当前 \((-a,b)\),且满足 \(0<b<a<2b\),我们就可以不断对 \(a\)\(b\) 减去 \(a-b\) 直到当前不满足 \(0<b<a<2a\) 为止。列方程易解得最多减去 \(\left\lceil\dfrac{2b-a}{a-b}\right\rceil\) 次。所以这个过程用除法 \(O(1)\) 计算即可。

这种算法的时间复杂度是 \(O(\log V)\) 的。

时间复杂度证明:(来自syh巨佬)

考虑证明 \(b\) 在上述综合算法下是指数级下降的。

设当前为 \((-a,b)\),其中 \(a,b>0\)。如果 \(a\) 不是 \(b\) 的倍数的话,显然会进行如下操作:

\[(-a,b)\xrightarrow{k}(-a+kb,b)\Rightarrow(-b,-a+kb) \]

(其中 \(k\) 为满足 \(-a+kb>0\) 的最小的 \(k\),显然 \(0<-a+kb<b\)

\(a'=b\)\(b'=-a+kb\),那么 \(b'<a'\)。分两种情况讨论:

  • \(b'\leq\dfrac{b}{2}\),那么 \(b'\leq\dfrac{b}{2}\)废话

  • \(b'>\dfrac{b}{2}\),则 \(b'>\dfrac{b}{2}=\dfrac{a'}{2}\),即 \(a'<2b'\),又 \(b'<a'\),所以满足 \(b'<a'<2b'\),可以进行刚刚讲到的优化算法:

    对于 \((a',b')\),不断对分母分子减去 \((a'-b')\) 直到当前的 \(b''\) 不满足 \(b''<a''<2b''\) 为止。

    由于我们证明过 \(a''-b''=a'-b'>0\),即 \(a''\) 恒大于 \(b''\) 的,所以当上述条件不满足时,必是 \(a''>2b''\)

    \(b''<\dfrac{a''}{2}<\dfrac{a'}{2}=\dfrac{b}{2}\)

所以无论如何,在每次 \(O(1)\) 的操作后,一定有 \(b'\leq \dfrac{b}{2}\)

所以时间复杂度为 \(O(\log V)\)

代码如下:

#include<bits/stdc++.h>

#define ll long long

using namespace std;

inline ll read()
{
	ll x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1ll)+(x<<3ll)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int t;
ll a,b,ans;

ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;
}

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		ans=0;
		a=read(),b=read();
		if(b<0) b=-b,a=-a;
		if(a>0)
		{
			swap(a,b),a=-a;
			ans++;
		}
		a=-a;
		while(a)
		{
			if(!(a%b))
			{
				ans+=a/b,a=0;
				continue;
			}
			if(b<a&&a<2ll*b)
			{
				ll tmp=(2ll*b-a)/(a-b);//这里应该用ceil,但我精度炸了,所以先向下取整再if判断一下 
				ll x=a-b;
				a-=tmp*x,b-=tmp*x;
				if(b<a&&a<2ll*b)
				{
					a-=x,b-=x;
					tmp++;
				}
				ans+=3ll*tmp;
			}
			else
			{
				ll tmp=a/b+1;
				ll t=a;
				a=b,b=-t+tmp*b;
				ans+=tmp+1;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}
/*
5
0 1
1 1
-1 2
-2 4
-8 -5
*/
posted @ 2022-10-30 14:02  ez_lcw  阅读(22)  评论(0编辑  收藏  举报