一直在学数论,结果菜到连个助攻都混不上
题目链接
题目
给定两个整数a,b,让你去找一个k,这个k使得lcm(a + k,b + k)最小
题解
考虑lcm的公式
lcm(a + k,b + k) = (a + k) * (b + k) / gcd(a + k,b + k)
我们将gcd(a + k,b + k)单独提取出来,
由辗转相除法,gcd(a,b)(a > b) = gcd(b,a % b)
设a > b,a = b + t,则t = a - b
gcd(a + k,b + k) = gcd(b + k + t,b + k) = gcd(t,b + k) = gcd(a - b,b + k)
带入lcm的公式,
我们注意到,a - b的因子是有限的,可以在sqrt(a - b)的时间内求出,
对于每一个因子p,我们可以找到多个b + k为最小的满足gcd(a - b,b + k) = p,
因为分母不变,所以最小的分子就是满足gcd(a - b,b + k) = p条件下的最优的k,
分子是随k递增的,所以最小的k就是最优的k
而如何去找最小的k呢,k = p - b % p就是最优的了(大于等于b的最小的p的倍数)
但问题来了,如果此时找到的b + k是p的倍数但是,gcd(a - b,b + k) = px呢?
我们再次回到lcm的公式,注意到,此时分母比最优解大,分子比最优解要小,所以找当前状态下的最优解没有意义
所以可以舍弃当前因子所限制的情况,为了简单处理,都计算一下比较保险,所以这个问题我们可以忽略它
所以题目分为以下几步:
① 找因子
② 找当前因子下最优的k
③ 判断最优答案
代码
#include <cstring>
#include <bits/stdc++.h>
using namespace std;
long long lcm(long long a,long long b,long long k)//求最小公倍数
{
return (a + k) * (b + k) / __gcd(a + k,b + k);
}
int main()
{
long long a,b;
cin >> a >> b;
if(a < b)
swap(a,b);
long long c = a - b;
long long ans = lcm(a,b,0),ans_id = 0;//初始化为k等于0的情况
for(long long i = 1;i <= c / i;i++)
if(c % i == 0)//找因子
{
long long x = i;
long long y = c / i;
//找最优的k
long long t1 = (x - b % x);
long long t2 = (y - b % y);
long long res = lcm(a,b,t1);
if(ans > res)
{
ans = res;
ans_id = t1;
}
res = lcm(a,b,t2);
if(ans > res)
{
ans = res;
ans_id = t2;
}
}
cout << ans_id << endl;
//cout << ans_id << " " << ans << endl;
}