题目链接:https://codeforces.com/contest/1152/problem/C
题意:现在有两个数n, m,你需要找到一个k,要求n+k, m+k的最小公倍数尽可能的小,如果有多个最小公倍数k要尽可能的小。
解题心得:
- l c m = n ∗ m / g c d lcm = n *m / gcd lcm=n∗m/gcd
- n+k和m+k的lcm要变小,那么加k之后肯定有更大的最大公因数,那么假设他们加上k之后拥有了公因数z
- 满足公式 ( n + k ) % z = ( m + k ) % z (n+k)\%z = (m+k)\%z (n+k)%z=(m+k)%z
- 公式可以展开化简变成 n % z = m % z n\%z = m\%z n%z=m%z
- 再合并一下变成: ( n − m ) % z = 0 (n-m)\%z = 0 (n−m)%z=0 (这里规定n>m)
- 这个公式说明n和m加上k之后产生的最大公因数必然是n-m的因子。这样就很简单了,直接把差值计算出来,然后将所有的因子找出来,每一次带入判定就行了。有一点很坑的就是k==0,这个时候也是最小公倍数最小的情况,需要特判一下不然WA63。
打比赛的时候脑子跟浆糊一样,公式都写出来就是反应不过来,好不容易反应过来了电脑卡崩了…
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
ll n, m;
vector <ll> prim;//存放n-m的素数因子
void get_prim(ll x) {
for(int i=2;i*i<=x;i++) {
while(x%i == 0) {
prim.push_back(i);
x/=i;
}
}
if(x != 1) prim.push_back(x);
}
ll get_factor(ll x) {
int pos = 0;
ll ans = 1;
while(x) {
if(x&1) {
ans *= prim[pos];
}
pos++;
x>>=1;
}
return ans;
}
int main() {
scanf("%lld%lld", &n, &m);
if(m < n) swap(n, m);
if(m % n == 0) {
puts("0");
return 0;
}
ll Min = LLONG_MAX, ans = LLONG_MAX;
ll minus = m - n, n1, m1;
get_prim(minus);
for(int i=1;i<(1<<prim.size());i++) {
ll temp = get_factor(i);//枚举所有的因子
n1 = (n/temp + 1) * temp;//n1 = n+k
m1 = (m/temp + 1) * temp;//m1 = m+k
ll temp2 = n1*m1/__gcd(n1, m1);
if(temp2 <= Min) {
if(temp2 < Min) {
ans = n1;
} else if(temp2 == Min && n1 < ans) { //都是最小最小公倍数的时候找小的
ans = n1;
}
Min = temp2;
}
}
//特判k == 0
ll temp = n*m/__gcd(n, m);
if(temp <= Min) {
ans = n;
}
cout<<ans-n<<endl;
return 0;
}