您的位置:首页 > 其它

POJ 2429 GCD & LCM Inverse(大整数素因子分解+二进制枚举)

2017-08-27 20:19 561 查看
GCD & LCM Inverse

Time Limit: 2000MSMemory Limit: 65536K
Total Submissions: 16540Accepted: 3061
Description
Given two positive integers a and b, we can easily calculate the greatest common divisor (GCD) and the least common multiple (LCM) of a and b. But what about the inverse? That is: given GCD and LCM, finding a and b.InputThe input contains multiple test cases, each of which contains two positive integers, the GCD and the LCM. You can assume that these two numbers are both less than 2^63.OutputFor each test case, output a and b in ascending order. If there are multiple solutions, output the pair with smallest a + b.Sample Input3 60Sample Output12 15

题目大意:

已知两个数的最大公约数 值为 a, 最小公倍数值为 b,现在求这两个数使得两个数的和最小,输出这两个数。

解题思路:

假设这两个数字为 x,y,那么有

{GCD(x,y)=aLCM(x,y)=b

其中 LCM(x,y)=x∗yGCD(x,y)=x∗ya

那么就有

n=xgcd(x,y)∗ygcd(x,y)=lcm(x,y)gcd(x,y)=ba

那么 n 一定是两个互素的数的乘积,假设为 aa,bb,那么我们只要解使得 aa+bb 值最小的 aa,bb 就OK了,最后在乘以 gcd(x,y) 就是结果。

那么我们首先将 n 进行素因子分解,但现在 n 是一个比较大的数,所以就要用到大因数分解 pollard_rho,因数分解之后假设:n=p1a1∗p2a2∗...∗pkak

那么我们需要保证的是两个数是互素的,所以两个数不能出现相同素因子,那么我们可以进行二进制枚举,判断这个素因子在 aa 中是否存在,然后找到 aa+bb 最小的 aa,bb。

代码:

#include <iostream>
#include <string.h>
#include <string>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <map>
using namespace std;
typedef long long LL;
const int times=20;//随机算法判定次数
map <LL, int>mp;//质因子的数目
LL Multi(LL a, LL b, LL MOD){
LL ans = 0;
while(b){
if(b & 1) ans = (ans + a) % MOD;
b>>=1;
a = (a + a) % MOD;
}
return ans;
}

LL Pow(LL a, LL b, LL MOD){
a = a % MOD;
LL ans = 1;
while(b){
if(b & 1) ans = Multi(ans, a, MOD);
b>>=1;
a = Multi(a, a, MOD);
}
return ans;
}

//若为合数返回true,不一定返回false
//a^(n-1)=1(mod n) -> a^((2^t)*x)=-1(mod n) == a^x=1(mod n)
bool test(LL a,LL n,LL x,LL t)//miLLer_rabin算法的核心
{
LL res = Pow(a,x,n);//a^x mod n
LL last = res;
for(int i=1;i<=t;i++){
res=Multi(res,res,n);//res=res*res mod n
if(res==1 && last!=1 && last!=n-1) return true;
last=res;
}
if(res!=1) return true;
return false;
}

//若为素数(或伪素数)返回true,合数返回false
bool miLLer_rabin(LL n){
if(n < 2) return false;
if(n==2) return true;
if((n&1)==0) return false; //偶数
LL x=n-1, t=0;
while((x&1)==0)//n-1=(2^t)*x;
{
x>>=1;
t++;
}
for(int i=0;i<times;i++)//进行随机判定
{
LL a=rand()%(n-1)+1;//随机找0~n-1的整数
if(test(a,n,x,t)) return false;
}
return true;
}

LL factor[100];//保存质因数分解结果
int tot;//记录质因数个数,下标从0开始

LL GCD(LL a, LL b){
if(a < 0) return GCD(-a, b);
if(b == 0) return a;
return GCD(b, a%b);
}

LL pollard_rho(LL x,LL c){
LL i=1,k=2;
LL x0=rand()%x;
LL y=x0;
while(1){
i++;
x0=(Multi(x0,x0,x)+c)%x;
LL d=GCD(y-x0,x);
if(d!=1&&d!=x) return d;
if(y==x0) return x;
if(i==k){
y=x0;
k+=k;
}
}
}
//对n进行素因子分解
void find_factor(LL n){
if(miLLer_rabin(n))//若为素数
{
factor[tot++]=n;
mp
++;
return ;
}
LL p=n;
while(p>=n)  p=pollard_rho(p,rand()%(n-1)+1);
find_factor(p);
find_factor(n/p);
}
LL pw(LL a, LL b){
LL ans = 1;
while(b){
if(b & 1) ans = ans*a;
b>>=1;
a*=a;
}
return ans;
}
int main(){
LL a, b;
while(cin>>a>>b){
if(a==b){printf("%lld %lld\n",a,b); continue;}
tot = 0;
mp.clear();
LL n = b/a;
find_factor(n);
sort(factor, factor+tot);
tot = unique(factor, factor+tot)-factor;
LL ans = b, aa, bb;
int all = (1<<tot);
for(int i=0; i<all; i++){
LL tp1 = 1, tp2 = 1;
for(int j=0; j<tot; j++){
if(i&(1<<j)){
LL tmp = pw(factor[j] , mp[factor[j]]);
tp1 *= tmp;
}
}
tp2 = n / tp1;
if(ans > tp1*a+tp2*a){
ans = tp1*a+tp2*a;
aa = min(tp1, tp2);
bb = max(tp1, tp2);
}
}
printf("%lld %lld\n",aa*a, bb*a);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: