题目链接
http://poj.org/problem?id=2429
题意
给两个数,分别是要求的两个数 a , b a,b a,b的 g c d gcd gcd和 l c m lcm lcm,要求出一组满足的 a a a和 b b b,且 a + b a+b a+b的值最小
思路
有一条性质是:
a ∗ b = g c d ( a , b ) ∗ l c m ( a , b ) a*b=gcd(a,b)*lcm(a,b) a∗b=gcd(a,b)∗lcm(a,b),
做下变换:
a g c d ( a , b ) ∗ b g c d ( a , b ) = l c m ( a , b ) g c d ( a , b ) \frac{a}{gcd(a,b)}*\frac{b}{gcd(a,b)}=\frac{lcm(a,b)}{gcd(a,b)} gcd(a,b)a∗gcd(a,b)b=gcd(a,b)lcm(a,b)
不妨令:
x = a g c d ( a , b ) x=\frac{a}{gcd(a,b)} x=gcd(a,b)a
y = b g c d ( a , b ) y=\frac{b}{gcd(a,b)} y=gcd(a,b)b
z = l c m ( a , b ) g c d ( a , b ) z=\frac{lcm(a,b)}{gcd(a,b)} z=gcd(a,b)lcm(a,b)
咱已经知道 z z z 了,所以可以对 z z z 进行质因子分解,然后重新分配因子到 x , y x,y x,y,从而求出 x , y x,y x,y,但z可能较大,所以需要用到 p o l l a r d _ r h o pollard\_rho pollard_rho算法和 M i l l e r _ R a b i n Miller\_Rabin Miller_Rabin搭配着求出 z z z 的质因子,但我们知道 x , y x,y x,y互质,所以,相同的质因子要合起来,之后再重新分配给 x , y x,y x,y,分配求和最小那组的过程可以用二进制枚举,也可以 d f s dfs dfs,分配之后输出: x ∗ g c d ( a , b ) x*gcd(a,b) x∗gcd(a,b)和 y ∗ g c d ( a , b ) y*gcd(a,b) y∗gcd(a,b)即可。注意一下特判 a , b a,b a,b相等的情况。
参考代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn=1e6+10;
const ll inf=1e12;
#define pb push_back
vector<ll> v;//存合并后的因子
ll mult_mod(ll a,ll b,ll c)//快速乘:ret=(a*b)%c
{
a%=c;
b%=c;
ll ret=0,tmp=a;
while(b)
{
if(b&1){
ret+=tmp;
if(ret>c)ret-=c;//直接取模慢
}
tmp<<=1;
if(tmp>c)tmp-=c;
b>>=1;
}
return ret;
}
ll qpow(ll x,ll n,ll mod){//快速幂:ret=(x^n)%mod
if(n==1)return x%mod;
ll tmp=x%mod;
ll ret=1;
while(n){
if(n&1)ret=mult_mod(ret,tmp,mod);
tmp=mult_mod(tmp,tmp,mod);
n>>=1;
}
return ret;
}
//通过a^(n-1)=1(mod n)判断n是否为素数
//n-1=x*2^t二次判断
//一定是合数则返回true,否则返回false
bool check(ll a,ll n,ll x,ll t){
ll ret=qpow(a,x,n);
ll last=ret;
for(int i=1;i<=t;i++){
ret=mult_mod(ret,ret,n);
if(ret==1&&last!=1&&last!=n-1)return true;
last=ret;
}
if(ret!=1)return true;
return false;
}
//Miller_Rabin快速判断一个ll范围内的数是否为素数
//是素数返回true,否则返回false
bool Miller(ll n){
if(n<2)return false;
if(n==2||n==3||n==5||n==7)return true;
if((n%2==0)||(n%3==0)||(n%5==0)||(n%7==0))return false;
ll x=n-1;
ll t=0;
while((x&1)==0){x>>=1;t++;}
srand((unsigned)time(NULL));
for(int i=0;i<10;i++){
ll a=rand()%(n-1)+1;
if(check(a,n,x,t))return false;
}
return true;
}
ll factor[107],tol;//存的无序质因子,[0,tol-1]
ll gcd(ll a,ll b){
ll t;
while(b){
t=a;
a=b;
b=t%b;
}
return a>=0?a:-a;
}
//找一个因子
ll pollard_rho(ll x,ll c){
ll i=1,k=2;
srand((unsigned)time(NULL));
ll x0=rand()%(x-1)+1;
ll y=x0;
while(1){
i++;
x0=(mult_mod(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进行质因子分解,存入factor
void findfac(ll n,int k)
{
if(n==1)return;
if(Miller(n))//素数
{
factor[tol++]=n;
return;
}
ll p=n;
int c=k;
while(p>=n)p=pollard_rho(p,c--);//防止死循环k
findfac(p,k);
findfac(n/p,k);
}
void solve(int x,int len,ll &a,ll &b){//二进制枚举
vector<int> s;
s.clear();
while(x){
s.pb(x%2);
x/=2;
}
for(int i=s.size();i<len;i++)s.pb(0);
ll l=1,r=1;
for(int i=0;i<len;i++){
if(s[i]==1)l*=v[i];
else r*=v[i];
}
if(l+r<a+b){
a=l,b=r;
}
}
int main(){
ll x,y;
while(cin>>x>>y){
if(x==y){//x、y相等时特判
cout<<x<<' '<<y<<'\n';continue;
}
tol=0;
findfac(y/x,107);
sort(factor,factor+tol);
v.clear();
v.pb(factor[0]);
for(int i=1;i<tol;i++){
if(factor[i]==factor[i-1]){
v.back()*=factor[i];
}
else v.pb(factor[i]);
}
int len=v.size();
int n=1<<len;
ll l=inf,r=inf;
for(int i=1;i<n;i++){//进行二进制枚举,选最小的一组
solve(i,len,l,r);
}
if(l>r)swap(l,r);
cout<<l*x<<' '<<r*x<<'\n';
}
return 0;
}