ARC159B GCD Subtraction

题目


这里有一个性质,对于互质的两个数 a , b a,b a,b,它们的答案与 a g , b g ag,bg ag,bg 两数的答案相等。设 a g , b g ag,bg ag,bg i i i 操作减去的数 x x x a , b a,b a,b i i i 次操作减去的数为 y y y,显然有 x = g y x=gy x=gy,前者减去的数是后者的 g g g 倍,而 a g , b g ag,bg ag,bg 又恰好是 a , b a,b a,b g g g 倍,得证。

所以我们可以先把 a , b a,b a,b 除以其最大公约数,变成互质的 a , b a,b a,b。此时最大公约数是 1 1 1,如果后面的最大公约数一直是 1 1 1,直接模拟必然超时。

对于特殊情况:如果 a = b a=b a=b,步数为 1 1 1;如果 a , b a,b a,b 有一者为 1 1 1,步数为 1 1 1;如果 ∣ a − b ∣ = 1 |a-b|=1 ab=1,步数为 min ⁡ ( a , b ) \min(a,b) min(a,b)

考虑一般情况:快速求出最小的 k k k,存在大于 1 1 1 的数 p p p 满足 gcd ⁡ ( a − k , b − k ) = p \gcd(a-k,b-k)=p gcd(ak,bk)=p。显然限定 p p p 为质数不会增大 k k k。枚举 1 0 6 10^6 106 以内的质数求出最小的 k k k(注意 k < min ⁡ ( a , b ) k<\min(a,b) k<min(a,b) 等细节),然后重复上面的流程即可。

分析时间复杂度:设 N = max ⁡ ( A , B ) N=\max(A,B) N=max(A,B)求最小的 k k k O ( N ln ⁡ N ) O(\frac{\sqrt N}{\ln N}) O(lnNN ) 的,每次 a , b a,b a,b 都要除以最大公约数,因此会有 O ( log ⁡ 2 N ) O(\log_2 N) O(log2N) 次流程,预处理质数 O ( N ) O(\sqrt N) O(N ),总的时间复杂度为 O ( N ) O(\sqrt N) O(N )

#include
using namespace std;
#define ll long long
const int N=1e6+1;
ll n,m,ans;
int a[N],p[N],cnt;
ll gcd(ll a,ll b)
{
	return !b?a:gcd(b,a%b);
}
int main()
{
	a[1]=1;
	for(int i=2;i<N;i++){
		if(!a[i]) p[++cnt]=i;
		for(int j=1;j<=cnt&&i*p[j]<N;j++){
			a[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
	cin>>n>>m;
	while(n&&m){
		if(n>m) swap(n,m);
		ll g=gcd(n,m);
		n/=g,m/=g;
		if(abs(n-m)<=1) cout<<ans+min(n,m),exit(0);
		ll k=1e12,P=0;
		for(int i=1;i<=cnt&&p[i]<=n;i++) if(p[i]<=n&&n%p[i]==m%p[i]) k=min(k,n%p[i]);
		if(k==1e12){
			ll x=abs(n-m);
			if(x<=n&&n%x==m%x) k=n%x;
			else k=n;
		}
		ans+=k;
		n-=k,m-=k;
	}
	cout<<ans;
}

你可能感兴趣的:(算法)