【POJ 2429】大数分解+二进制枚举

1.题目链接。题目大意:给出两个数的gcd和lcm,求这两个数和最小是多少?

2.分析:\\gcd(x,y)=a\\ lcm(x,y)=b\\。那么我们可以简单的推导一下:

 

                                            \\because:\\ lcm(x,y)=\frac{x*y}{gcd(x,y)}=b\\ so: \frac{b}{a}=n

这里n是一个整数,并且:

                                     n=\frac{x}{gcd(x,y)}*\frac{y}{gcd(x,y)}

说明n一定是由两个互质的数乘起来的。那么我们枚举n的因子,然后找一下最小的即可。但是这里似乎有问题,因为n可以到1e18.对于一个数的分解,我们可以做到sqrt(n)。显然不行。从另外一个角度考虑,因子是用这个数进行素因子分解之后产生的,两个互质就是说二者拥有不同的素因子。如果我们能够找到这个数所有的素因子,然后二进制枚举一下,因为longlong范围内,一个数的素因子的种类不会超过20种。但是对于一个大整数进行素因子分解也是np困难的。不过有随机分解算法,这样就可以在期望复杂度不高的情况下解决这个问题。

#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;
}

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;
}

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[n]++;
		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;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值