GCD & LCM Inverse //数论

题目链接

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) ab=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)agcd(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) xgcd(a,b) y ∗ g c d ( a , b ) y*gcd(a,b) ygcd(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值