數論學習小記 其之三 Gcd與Lcm

關於Gcd的歐幾里得算法,以及Gcd與Lcm的關係在《數論學習小記 其之一》中有記錄,這裏就不重複了。html

常常作一些與Gcd和Lcm有關的題目,思路有許多相近之處,放在一塊兒總結,遇到新題會繼續更新。java

一個結論

摘自:如何證實gcd(a,b) = gcd(a+b, lcm(a,b))_百度知道c++

QQ截圖20140122100415

Uva 11388 GCD LCM

題意:給定兩個整數G和L,找出兩個整數a和b,使得兩者的最大公約數爲G,最小公倍數爲L,若是有多組解,輸出a<=b且a最小的解,若無解輸出-1算法

思路:根據Gcd和Lcm的性質可知:L=(a*b)/G,則a*b=L*G,因爲G是a和b的約數,所以a和b能夠寫成G*x,G*y,則等式變爲:L/G=x*y。若L%G!=0,則無解,不然取x爲1便可。也就是說,若是有解,最小就是G,而後另外一個數就是L。學習

#include <cstdio>

int Gcd (int a,int b)
{
    return !b?a:Gcd(b,a%b);
}

int main ()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if (b%a==0)
            printf("%d %d\n",a,b);
        else
            printf("-1\n");
    }
    return 0;
}

Zoj 1577 GCD & LCM

很經典的一道題,本題參考了url

ZOJ 1577 GCD & LCM - bingshen的專欄 - 博客頻道 - CSDN.NETspa

zoj 1577 gcd(x,y) * lcm(x,y) = x*y - NeverLoseHeart. - 博客頻道 - CSDN.NET.net

題意:給出兩個數x,y,求有多少組p,q,知足gcd(p,q) = x且 lcm(p,q) = y。code

無標題

兩個很易得的結論:htm

lcm(x,y)/ gcd(x,y)  = (x / gcd(x,y)) * (y / gcd(x,y))

lcm(x,y)* gcd(x,y)  = x*y

假設g=gcd(p,q),l=lcm(p,q),key=l/g,若是l%g!=0則無解。

l是p和q公共的素因子的並集,而g則是交集(圖中紅色部分),因而key的意思就是l恰好比g多的一部分素因子(圖中藍色部分)

因而咱們能夠選擇這些素因子和g組合獲得了p,另外一部分和g組合獲得了q

假設key的素因子個數(重複的不算)有n個,那麼選擇方案就有:C(n,0)+C(n,1)+C(n,2)+...+C(n,n),這個式子的和爲2^n。

重複的因子不算個數,由於同1個素因子必須所有在p中或者所有在q中,若是同時屬於p和q,GCD將增大。

本題不用篩素也能夠。

#include <cstdio>

const int N=1200;

int prime[N],np=0;
bool tag[N];

void Prime ()
{
    for (int i=2;i<N;i++) if (tag[i]==false)
    {
        prime[np++]=i;
        for (int j=i+i;j<N;j+=i)
            tag[j]=true;
    }
}

int split (int n)   //計算素因子(不重複)的個數
{
    int ans=0;
    for (int i=0;prime[i]*prime[i]<=n;i++)
    {
        if (n%prime[i]==0)
        {
            ans++;
            while (n%prime[i]==0)
                n/=prime[i];
        }
    }
    if (n>1)
        ans++;
    return ans;
}

int main ()
{
    int a,b;
    Prime ();
    while (~scanf("%d%d",&a,&b))
    {
        if (b%a!=0)
        {
            printf("0\n");    //有可能會是0種組合
            continue;
        }
        int t=split(b/a);
        int ans=1;
        for (int i=1;i<=t;i++)
            ans*=2;
        printf("%d\n",ans);
    }
    return 0;
}

Hoj 2010 GCD & LCM Inverse

題目連接:http://acm.hit.edu.cn/hoj/problem/view?id=2010

題意:已知兩個數的gcd和lcm求這兩個數a, b,多解時要求a+b最小

思路:a/gcd * b/gcd = lcm/gcd,還有一個常識性質的性質:a*b爲定值時,a和b相差越小,a+b越小。因此從lcm/gcd的平方根開始枚舉a便可。

#include <cstdio>
#include <cmath>

long long Gcd (long long a,long long b)
{
    return !b?a:Gcd(b,a%b);
}

int main ()
{
    long long a,b;
    while (~scanf("%lld%lld",&a,&b))
    {
        long long tmp=b/a,i;
        for (i=sqrt(1.0*tmp);i>=1;i--)
            if (tmp%i == 0)
            {
                b=tmp/i;
                if (Gcd(i,b) == 1)
                    break;
            }
        printf("%lld %lld\n",a*i,a*b);
    }
    return 0;
}

uva 10791 - Minimum Sum LCM

題意:求幾個最小公倍數爲n的數的最小和

參考了:http://blog.csdn.net/goomaple/article/details/8550381

兩個最小公倍數爲n的數的和不必定最小,例如30=(5*6)=(2*3*5)  5+6>2+3+5

應該是n的各個質因子的相應次方數之和,但要注意幾個細節:
(1)當n = 1時,應輸出2(1*1=1,sum=1+1=2);
(2)當n是素數的時候,輸出n+1(n*1=n,sum=n+1);
(3)當只有單質因子時,sum=質因子相應次方+1;
(4)當N=2147483647時,它是一個素數,此時輸出2147483648,可是它超過int範圍,應考慮用long long。

#include <cstdio>

const int N=100005;

long long p[N],pNum[N],Num;
int prime[N],np=0;
bool tag[N];

void Prime ()
{
    for (int i=2;i<N;i++) if (tag[i]==false)
    {
        prime[np++]=i;
        for (int j=i+i;j<N;j+=i)
            tag[j]=true;
    }
}

void split (int n)
{
    Num=0;//np爲素數個數,注意int相乘有可能會超int可表示的範圍
    for (int i=0;i<np && (long long)prime[i]*prime[i]<=n;i++)
    {
        if (n%prime[i]==0)
        {
            p[Num]=prime[i];
            pNum[Num]=0;
            while(n%prime[i]==0)
            {
               pNum[Num]++;
               n/=prime[i];
            }
            Num++;
        }
    }
    if (n>1) p[Num]=n,pNum[Num++]=1;
}

int POW (int n,int index)
{//乘方次數不會很大,不必二分計算
    int ans=1;
    for (int i=1;i<=index;i++)
        ans*=n;
    return ans;
}

int main ()
{
    int n,Cas=1;
    Prime ();
    while (scanf("%d",&n),n)
    {
        printf("Case %d: ",Cas++);
        if (n==1)   //////
        {
            printf("2\n");
            continue;
        }
        split(n);
        if (Num==1)  //只有一個質因子(素數和素數的倍數)
        {
            printf("%lld\n",(long long)POW(p[0],pNum[0])+1); //由於至少要兩個數,另外一個數是1
            continue;
        }
        int ans=0;
        for (int i=0;i<Num;i++)
            ans+=POW(p[i],pNum[i]);
        printf("%d\n",ans);
    }
    return 0;
}
//1822500000=2^5 * 3^6 * 5^7

poj 2429 GCD & LCM Inverse

題意和上面Hoj的那道題徹底同樣,只是數據規模變大,若是用c++寫,那麼必需要用Pollard_rho大數因子分解,這部分我還不熟練,待熟練以後回來補上。

若是用java寫,那時間比較寬鬆,是能夠過的,c++時限2000ms,下面的java代碼 5516ms 過了……。

如下的java代碼摘自:poj 2429 數學題 簡單解法 - Because Of You - 博客園

import java.util.*;
import java.io.*;
import java.lang.Math;
public class Main{
    static long gcd(long a,long b)
    {
        long tmp;
        while(b!=0)
        {
            a%=b;
            tmp=a;
            a=b;
            b=tmp;
        }
        return a;
    }
    static long lcm(long a,long b)
    {
        return a/gcd(a,b)*b;
    }
    public static void main(String args[]){
        Scanner cin = new Scanner (System.in);
        long a,b,c,i;
        while(cin.hasNext())
        {
            a=cin.nextLong();
            b=cin.nextLong();
            c=b/a;
            for(i=(long)Math.sqrt(c);i>=1;i--)//a*i+b/i隨i的增大而減少
            {
                if(c%i==0&&gcd(i,c/i)==1)
                {
                    System.out.println((a*i)+" "+(b/i));//b/i確定大於a*i
                    break;
                }
            }
        }
    }
}

遺留問題

LightOJ 1289 涉及節省空間的素數篩法,會在下一篇博文中總結,也能夠參考我再csdn的題解:

LightOJ 1289 LCM from 1 to n (節省空間的素數篩法+n個數的最小公倍數) - whyorwhnt的專欄 - 博客頻道 - CSDN.NET

LightOJ 1215 尚未仔細想,這裏有篇題解:lightoj 1215 - 妮king狼 - 博客園

相關文章
相關標籤/搜索