POJ2429 GCD & LCM Inverse - 数论 - 质因子分解 - Pollard-pho算法

GCD & LCM Inverse

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 20548   Accepted: 3774

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.

Input

The 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.

Output

For each test case, output a and b in ascending order. If there are multiple solutions, output the pair with smallest a + b.

Sample Input

3 60

Sample Output

12 15

 
 

题目大概意思:

给出两个正整数 x , y ( 1 x y 2 63 ) x,y(1≤x≤y≤2^{63}) ,其中 y y 能够被 x x 整除,求两个正整数 a , b a,b ,使得 g c d gcd ( a , b ) = x (a,b)=x l c m lcm ( a , b ) = y (a,b)=y .若存在多组 a , b a,b ,则输出 a + b a+b 最小的一组。

 
 

分析:

g c d ( a , b ) = x gcd(a,b)=x 意味着 g c d ( a x , b x ) = 1 gcd(\frac{a}{x},\frac{b}{x})=1 ,即 a x \frac{a}{x} b x \frac{b}{x} 互质,因此 l c m ( a x , b x ) = a b x 2 g c d ( a x , b x ) = 1 x a b x = y x lcm(\frac{a}{x},\frac{b}{x})=\frac{\frac{ab}{x^2}}{gcd(\frac{a}{x},\frac{b}{x})}=\frac{1}{x}·\frac{ab}{x}=\frac{y}{x}

不妨设 u = a x , v = b x , w = y x u=\frac{a}{x},v=\frac{b}{x},w=\frac{y}{x} ,则求满足 g c d ( a , b ) = x , l c m ( a , b ) = y gcd(a,b)=x,lcm(a,b)=y 的和最小的 a , b a,b 这一问题可以转化为求满足 g c d ( u , v ) = 1 , l c m ( u , v ) = w gcd(u,v)=1,lcm(u,v)=w 的和最小的 u , v u,v .

为使 g c d ( u , v ) = 1 gcd(u,v)=1 u u v v 不能有相同的质因子;为了使 l c m ( u , v ) = w lcm(u,v)=w ,一定有 u v = w uv=w .

因此,如果把 w w 进行质因子分解,则 w w 可以被表示成若干质数的乘积的形式:

w = k = 1 n p k e k w=\prod_{k=1}^{n}{p^{e_k}_k}

则解一定可以被表示成这样的形式:

u = t = 1 s p i t e i t = p i 1 e i 1 p i 2 e i 2 p i s e i s v = t = s + 1 n p i t e i t = p i s + 1 e s + 1 p i s + 2 e s + 2 p i n e n \begin{aligned} u=&\prod_{t=1}^s{p_{i_t}^{e_{i_t}}}=p_{i_1}^{e_{i_1}}·p_{i_2}^{e_{i_2}}·\dots·p_{i_s}^{e_{i_s}}\\\\ v=&\prod_{t=s+1}^{n}{p_{i_t}^{e_{i_t}}}=p_{i_{s+1}}^{e_{s+1}}·p_{i_{s+2}}^{e_{s+2}}·\dots·p_{i_n}^{e_n}\\\\ \end{aligned}

由于 b 2 63 b≤2^{63} ,而前 17 17 个质数的乘积已经大于 2 63 2^{63} ,因此最多有不超过 16 16 个不同的质因子,即上式中 n 16 n≤16 ,可以通过枚举所有可能的组合在 O ( 2 n 1 ) O(2^{n-1}) 的时间复杂度内来找到和最小的 u , v u,v .

因此,如果我们可以对 w w 也就是 y x \frac{y}{x} 进行质因子分解,那么就可以得到最终答案。

朴素的方法是通过对不超过 w \sqrt{w} 的所有质数进行试除来分解质因子,可这里的可能 w w 高达 2 63 2^{63} ,朴素的算法无法在时间和内存限制内得到答案。

因此需要采用时间和空间上都更为优秀的 P o l l a r d p h o Pollard-pho 算法 来对 w w 进行质因子分解, P o l l a r d p h o Pollard-pho 算法的期望时间复杂度为 O ( b 1 4 ) O(b^{\frac{1}{4}}) ,因此算法的总复杂度为 O ( b 1 4 + 2 n 1 ) O(b^{\frac{1}{4}}+2^{n-1}) .

 
 
下面贴代码:

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <climits>
#include <algorithm>
using namespace std;


typedef unsigned long long ull;

const int MAX_N = 1000;
const int S = 20;

ull factor[MAX_N];
int fcnt;

ull gcd(ull a, ull b);                   // 计算 gcd(a, b)
ull mult_mod(ull a, ull b, ull m);       // 计算 a*b % m
ull pow_mod(ull x, ull p, ull m);        // 计算 x^p % m
bool check(ull a, ull n, ull x, ull t);  // 进行一轮检测, 确定是合数返回 true, 否则返回 false
bool Miller_Rabin(ull n);                // 是素数返回 true, 否则返回 false
ull Pollard_rho(ull x, ull c);
void find_factor(ull x);                 // 对 x 进行素数分解, 调用前应将 fcnt 初始化为0


ull fp[MAX_N];

int main()
{
	ull a, b;
	srand(time(0));

	while (~scanf("%llu%llu", &a, &b))
	{
		if (a == b)
		{
			printf("%llu %llu\n", a, b);
			continue;
		}

		b /= a;
		fcnt = 0;
		find_factor(b);
		sort(factor, factor + fcnt);
		fcnt = unique(factor, factor + fcnt) - factor;

		for (int i = 0; i < fcnt; ++i)
		{
			ull& cur = fp[i];
			cur = factor[i];
			while (!(b % cur))
			{
				cur *= factor[i];
			}
			cur /= factor[i];
		}
		ull x, y, mt = ULLONG_MAX;
		for (int i = (1 << fcnt) - 1; i >= 0; --i)
		{
			ull tx = 1, ty = 1;
			for (int j = 0; j < fcnt; ++j)
			{
				if (i >> j & 1)
				{
					tx *= fp[j];
				}
				else
				{
					ty *= fp[j];
				}
			}
			if (tx + ty < mt)
			{
				mt = tx + ty;
				x = min(tx, ty);
				y = max(tx, ty);
			}
		}
		printf("%llu %llu\n", x * a, y * a);
	}

	return 0;
}


ull gcd(ull a, ull b)
{
	if (a == 0)return 1;
	while (b)
	{
		ull t = a % b;
		a = b;
		b = t;
	}
	return a;
}

ull mult_mod(ull a, ull b, ull m)
{
	a %= m;
	b %= m;
	ull res = 0;
	while (b)
	{
		if (b & 1)
		{
			res = (res + a) % m;
		}
		a <<= 1;
		if (a >= m) a -= m;
		b >>= 1;
	}
	return res;
}

ull pow_mod(ull x, ull p, ull m)
{
	ull res = 1;
	x %= m;
	while (p)
	{
		if (p & 1)
		{
			res = mult_mod(res, x, m);
		}
		x = mult_mod(x, x, m);
		p >>= 1;
	}
	return res;
}

bool check(ull a, ull n, ull x, ull t)
{
	ull res = pow_mod(a, x, n);
	ull last = res;
	for (int i = 1; i <= t; i++)
	{
		res = mult_mod(res, res, n);
		if (res == 1 && last != 1 && last != n - 1) return true;
		last = res;
	}
	if (res != 1) return true;
	return false;
}

bool Miller_Rabin(ull n)
{
	if (n < 2) return false;
	if (n == 2) return true;
	if (!(n & 1)) return false;
	ull x = n - 1;
	ull t = 0;
	while (!(x & 1))
	{
		x >>= 1;
		++t;
	}
	for (int i = 0; i < S; i++)
	{
		ull a = rand() % (n - 1) + 1;
		if (check(a, n, x, t))
			return false;
	}
	return true;
}

ull Pollard_rho(ull x, ull c)
{
	ull i = 1, k = 2;
	ull x0 = rand() % x;
	ull y = x0;
	while (1)
	{
		i++;
		x0 = (mult_mod(x0, x0, x) + c) % x;
		ull d = gcd(y > x0 ? y - x0 : x0 - y, x);
		if (d != 1 && d != x) return d;
		if (y == x0) return x;
		if (i == k)
		{
			y = x0;
			k += k;
		}
	}
}

void find_factor(ull x)
{
	if (Miller_Rabin(x))
	{
		factor[fcnt++] = x;
		return;
	}
	ull p = x;
	while (p >= x)p = Pollard_rho(p, rand() % (x - 1) + 1);
	find_factor(p);
	find_factor(x / p);
}

原创文章 42 获赞 22 访问量 3042

猜你喜欢

转载自blog.csdn.net/weixin_44327262/article/details/98085416