题目:
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 17978 | Accepted: 3331 |
Description
Input
Output
Sample Input
3 60
Sample Output
12 15
题意:输入两个数的最大公约数和最小公倍数,求这两个数。如果有多组,输出和最小的一对。
思路:
设这两个数分别为:x , y。
那么 必然有 gcd(x,y)*k1 = x ; gcd(x,y)*k2 = y。
由 :x*y / gcd(x,y)= lcm(x,y) 变形得=>> x*y = lcm*gcd;
则:(gcd*k1) * (gcd*k2) = lcm*gcd.
化简得:k1*k2 = lcm/gcd
若我们找到一组k1,k2,满足:k1*k2 = lcm/gcd 的关系,那么k1*gcd就是可能的x,k2*gcd就是可能的y。
我们还应该注意到由于 k1 = x/gcd , k2 = y/gcd 。那么一定有gcd(k1,k2)=1 。
所以我们可以将lcm/gcd进行质因数分解,之后将质因数分别再凑为两堆,一堆相乘结果就是x ,令一堆就是y。特别要注意的是,由于gcd(k1,k2)=1 ,那么为了保证这个性质,分解出的质因数若存在多个,要再乘回去,否则gcd(k1,k2)就会等于那个质因数了。
题设还有最后一个条件:输出和最小的一对。由于x*y =gcd*lcm , 那么求和最小的一对也就是求差最小的一对解。而要使,差最小,无非就是使分解出的两个数都接近sqrt(lcm/gcd),所以我们将所有质因数的组合进行dfs,找到最优解即可。
代码:
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<set>
typedef long long ll;
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 5500;
const int Times = 10;
ll ct,cnt;
ll fac[N];
ll f[N];
ll ans;
ll gcd(ll a , ll b){
return b==0? a:gcd(b,a%b);
}
ll multi(ll a , ll b , ll m){
ll ans = 0;
a %= m;
while(b){
if(b&1){
ans = (ans+a)%m;
b--;
}
b >>= 1;
a = (a+a)%m;
}
return ans;
}
ll quick_mod(ll a , ll b , ll m){
ll ans = 1;
a %= m;
while(b){
if(b&1){
ans = multi(ans , a , m);
b--;
}
b>>=1;
a = multi(a,a,m);
}
return ans;
}
bool Miller_Rabin(ll n){
if(n==2) return true;
if(n<2 || !(n&1)) return false;
ll m = n-1;
int k = 0;
while((m&1)==0){
k++;
m>>=1;
}
for(int i = 0 ; i<Times ; i++){
ll a = rand()%(n-1)+1;
ll x = quick_mod(a,m,n);
ll y = 0;
for(int j = 0 ; j<k ; j++){
y = multi(x,x,n);
if(y==1 && x!=1 && x!=n-1) return false;
x = y;
}
if(y!=1) return false;
}
return true;
}
ll pollard_rho(ll n , ll c){
ll i = 1 , k=2;
ll x = rand()%(n-1)+1;
ll y = x;
while(true){
i++;
x = (multi(x,x,n)+c)%n;
ll d = gcd((y-x+n)%n , n);
if(1<d && d<n) return d;
if(y == x) return n;
if(i == k){
y = x;
k <<= 1;
}
}
}
void find(ll n , int c){
if(n == 1) return;
if(Miller_Rabin(n)){
fac[ct++] = n;
return;
}
ll p = n;
ll k = c;
while(p >= n) p = pollard_rho(p , c--);
find(p , k);
find(n/p , k);
}
void findx(ll i , ll x , ll q){
if(i==cnt) return;
if(x>ans && x<=q) ans = x;
findx(i+1 , x , q);
x *= f[i];
if(x>ans && x<=q) ans = x;
findx(i+1 , x , q);
}
int main(){
ll a,b;
while(scanf("%lld %lld",&a,&b)!=EOF){
if(a == b){
printf("%lld %lld\n",a,b);
continue;
}
ct = 0;
ll n = b/a;
find(n,120);
sort(fac,fac+ct);
f[0] = fac[0]; cnt = 0;
for(int i = 1 ; i<ct ; i++){
if(fac[i] == fac[i-1]){
f[cnt] = f[cnt] * fac[i];
}
else{
cnt++;
f[cnt] = fac[i];
}
}
cnt++;
ll goal = (ll)sqrt(n*1.0);
ans = 1;
findx(0,1,goal);
printf("%lld %lld\n" , ans*a , n/ans*a);
}
return 0;
}