GCD&LCM

1.青蛙的約會

題目://poj.org/problem?id=1061

題解:(x+mt)%L==(y+nt)%L等價於x-y+(m-n)t=kL

因此我們整理一下:(m-n)t+kL=y-x

a=m-n,b=L,c=y-x

在這裡需要注意a的值不為負,如果為負,則a=-a,c=-c

然後套用公式求出最小的t:t=(c/g*x%(b/g)+b/g)%(b/g)

程式碼:

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;

ll extend_gcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    ll res=extend_gcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return res;
}

int main()
{
    ll i,j,x,y,m,n,l,a,b,c;
    cin>>x>>y>>m>>n>>l;
    a=m-n;b=l;c=y-x;
    if(a<0)
    {
        a=-a;
        c=-c;
    }
    ll g=extend_gcd(a,b,x,y);
    if(c%g!=0)
    {
        puts("Impossible");
    }
    else
    {
        cout<<(c/g*x%(b/g)+b/g)%(b/g)<<endl;
    }
    return 0;
}

 

2.Least Common Multiple

題目://acm.hdu.edu.cn/showproblem.php?pid=1019

題解:每兩個之間求一遍lcm,一共球n次

程式碼:

#include<iostream>
using namespace std;
typedef long long ll;
ll t,a[100000],n;
ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}
ll lcm(ll a,ll b)
{
    return a*b/gcd(a,b);
}

int main()
{
    ll i,j;
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(i=0;i<n;i++)
            cin>>a[i];
        ll res=a[0];
        for(i=1;i<n;i++)
        {
            res=lcm(res,a[i]);
        }
        cout<<res<<endl;
    }
    return 0;
}

 

3. A/B

題目://acm.hdu.edu.cn/showproblem.php?pid=1576

題解:逆元的應用

程式碼:

#include<iostream>
using namespace std;
typedef long long ll;
ll extend_gcd(ll a,ll b,ll& x,ll& y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll res=extend_gcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
}

ll mod_inverse(ll a,ll m)
{
    ll x,y;
    extend_gcd(a,m,x,y);
    return (m+x%m)%m;
}

int main()
{
    ll i,j,t,n,b;
    ll mod=9973;
    cin>>t;
    while(t--)
    {
        cin>>n>>b;
        cout<<(n*mod_inverse(b,mod))%9973<<endl;
    }
    return 0;
}

 

4.又見GCD

題目://acm.hdu.edu.cn/showproblem.php?pid=2504

題解:a和c的最大公約數為b,因此c的選取從2*b開始,與a計算gcd,如果不是b就加b,如此往複

程式碼:

 

#include<iostream>
using namespace std;

int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}

int main()
{
    int i,j,n,a,b,c;
    cin>>n;
    while(n--)
    {
        cin>>a>>b;
        c=b*2;
        while(gcd(a,c)!=b)
        {
            c+=b;
        }
        cout<<c<<endl;
    }
    return 0;
}

 

5.GCD

題目://acm.hdu.edu.cn/showproblem.php?pid=2588

題解:

給定N,M求gcd(i,N)>=M的i的個數

我們可以分解N=a*b, i=a*d(b>=d 且b,d互質),那麼我們要求的就是a》=m的時候d的個數(b隨a而確定)

由於b>=d且b,d互質,所以這個數目就是φ(b)-1  

但是,如果對於每個a枚舉b,鐵定超時。(仍然O((N-M)*sqrt(N))的複雜度)

 

但是如果單純這樣全部枚舉的話依舊會超時,所以我們要想一個辦法去優化它。

我們可以折半枚舉,這裡的折半並不是二分的意思。

我們先看,我們枚舉時,當i<sqrt(n),假設a=n / i, 當i>sqrt(n)之後 有b=n/i,我們觀察到當n%i==0時,會出現一種情況,就是a*b==n。所以我們就可以只需要枚舉sqrt(n)種情況,然後和它對應的情況就是 n/i。

我們這種枚舉時間會快非常多

 

程式碼:

#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
ll phi(ll n)
{
    ll ans=n;
    for(ll i=2;i<=sqrt(n);i++)
    {
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0)
                n/=i;
        }
    }
    if(n>1)
        ans=ans/n*(n-1);
    return ans;
}
int main()
{
    int t;
    ll n,m;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        ll ans=0;
        for(ll i=1;i*i<=n;i++)
        {
            if(n%i==0)
            {
                if(i>=m)
                    ans+=phi(n/i);//計算sqrt(n)左邊的
                if(i*i!=n&&n/i>=m)
                    ans+=phi(i);//計算計算sqrt(n)右邊的i*i==n時,在上個語句已經執行 (避免)完全平方算兩次
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

Tags: