GCD & LCM Inverse

http://poj.org/problem?id=2429

大致题意:给出两个数的最大公约数a和最小公倍数b,求这两个数,并且要这两个数加起来最小。

方法:b/a求得的值是两个互质的数的乘积, 这题的数比较大, 所以用了模板、

#include <stdio.h>  
#include <string.h>  
#include <time.h>  
#include <algorithm>  
using namespace std;  
#define Time 12 // Miller测试次数  
typedef __int64 ll;  
  
const ll INF = (1LL << 62) + ((1LL<<62)-1);  
const int maxC = 240;  
  
ll big_mul(ll a, ll b, ll m) {  
    ll ret = 0;  
    a %= m;  
    while(b) {  
        if(b & 1) {  
            ret += a;  
            if(ret >= m)    ret -= m;  
        }  
        a *= 2;  
        if(a >= m)  a -= m;  
        b /= 2;  
    }  
    return ret;  
}  
  
ll pow_mod(ll x, ll n, ll m) {  
    ll ret = 1;  
    x %= m;  
    while(n) {  
        if(n & 1)   ret = big_mul(ret, x, m);  
        x = big_mul(x, x, m);  
        n /= 2;  
    }  
    return ret;  
}  
  
// 以a为基对n进行Miller次测试并进行二次探测,返回true则是合数  
bool Wintess(ll a, ll n) {  
    ll m = n-1;  
    int top = 0;  
    // n-1 = m*(2^top)  
    while(m % 2 == 0) {  
        m /= 2;  
        top++;  
    }  
    ll x = pow_mod(a, m, n), y;  
    for(int i = 0;i < top; i++) {  
        y = big_mul(x, x, n);  
        if(y == 1 && (x != 1 && x != n-1))  
            return true;  
        x = y;  
    }  
    if(y > 1)   return true;  
    return false;  
}  
  
// 对n进行ts次 Miller素性测试  
bool Miller_Rabin(int ts, ll n) {  
    if(n == 2)  return true;  
    if(n == 1 || n % 2 == 0)  return false;  
    srand(time(NULL));  
    for(int i = 0;i < ts; i++) {  
        ll a = rand() % (n-1) + 1;  
        if(Wintess(a, n))   return false;  
    }  
    return true;  
}  
  
ll ans;  
  
ll gcd(ll a, ll b) {  
    return b ? gcd(b, a%b) : a;  
}  
  
// 对n进行因式分解,找出n的一个因子,该因子不一定是最小的  
ll Pollard(ll n, int c) {  
    srand(time(NULL));  
    ll i = 1, k = 2, x = rand()%n, y = x;  
    while(true) {  
        i++;  
        x = (big_mul(x, x, n) + c) % n;  
        ll d = gcd(y - x, n);  
        if(d > 1 && d < n)  return d;  
        if(y == x)  return n; // 如果该数已经出现过,直接返回  
        if(i == k) {  
            y = x; k <<= 1;  
        }  
    }  
}  
  
// 找出所有素因子  
void solve(ll n, int c) {  
    if(n == 1)  return ;  
    // 判断是否为素数  
    if(Miller_Rabin(Time, n)) {  
        if(ans > n) ans = n;  
        return ;  
    }  
    ll m = n;  
    while(m == n) {   // 找出n的一个因子  
        m = Pollard(n, c--);  
    }  
    solve(m, c);  
    solve(n/m, c);  
}  
  
int main() {  
    ll pp, qq;
    ll num[52];
    while(scanf("%I64d%I64d" , &pp, &qq) == 2) {  
        int i, j;
        ll tt = qq / pp;
        int tot = 0;
        for(i = 1; i <= 50; i++)
            num[i] = 1;
        //Miller_Rabin(Time, tt)这个函数是用来判断tt是否为质数
        while(!Miller_Rabin(Time, tt)){
            if(tt == 1)
                break;
            //solve(tt, maxC)这个函数是用来求tt 的最小的质因数,ans就是这个质因数
            ans = INF;
            solve(tt, maxC);
            tot++;
            while(tt%ans == 0){
                num[tot] *= ans;
                tt /= ans;
            }
        }  
        if(tt > 1)
            num[++tot] = tt;
        ll res = INF;
        int temp = 1;
        for(i = 1; i <= tot; i++)
            temp *= 2;
        ll p, q;
        ll ansp, ansq;
        for(i = 0; i < temp; i++){
            p = q = 1;
            if((i&1))
                p *= num[1];
            else
                q *= num[1];
            for(j = 1; j < tot; j++){
                if((i>>j)&1){
                    p *= num[j+1];
                   // printf("p = %I64d\n", p);
                }
                else{
                    q *= num[j+1];
                    //printf("q = %I64d\n", q);
                }
            }
            if(p+q < res){
                ansp = p;
                ansq = q;
                res = p+q;
            }
        }
        if(ansp > ansq) swap(ansp, ansq);
        printf("%I64d %I64d\n",ansp*pp, ansq*pp);
    }  
    return 0;  
}  
  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值