zoukankan      html  css  js  c++  java
  • 最大公约数与最小公倍数问题

    输入二个正整数x0,y0(2≤x0≤100000,2≤y0≤1000000),求出满足下列条件的P、Q的个数。 

    条件:1.P、Q是正整数
    二要求P、Q以xO为最大公约数,以yO为最小公倍数。
    试求,满足条件的所有可能的两个正整数的个数。
    输入

    样例输入
    3 60
    样例输出
    4
    此时的P Q 分别为
    3 60
    15 12
    12 15
    60 3
    所以,满足条件的所有可能的两个正整数的个数共4种。

    Sol1:
    采用最简单的方法,暴力枚举所求的[i,j]这样的一对数字
    时间复杂度为O(N*N*Log(N)).
    由于原数据太小了,只TLE了一个点

    #include<bits/stdc++.h>
    using namespace std;
    int gcd(int a,int b)
    {
    if(b==0)
    return a;
    return gcd(b,a%b);
    }
    int main()
    {
    int x,y,a=0;
    cin>>x>>y;
    for(int i=x;i<=y;i++)
    for (int j=x;j<=y;j++)
    if (gcd(i,j)==x)
    {
    int t=i*j;
    if (t%gcd(i,j)==0&&t/gcd(i,j)==y)
    a++;
    }
    cout<<a<<endl;
    }

    Sol2:在枚举时,强制规定j>i,最终结果输出时ans=ans*2;
    #include<bits/stdc++.h>
    using namespace std;
    int gcd(int a,int b)
    {
    if(b==0)
    return a;
    return gcd(b,a%b);
    }
    int main()
    {
    int x,y,ans=0;
    cin>>x>>y;
    for(int i=x;i<=y;i++)
    for (int j=i+1;j<=y;j++)
    if (gcd(i,j)==x)
    {
    int t=i*j;
    if (t%gcd(i,j)==0&&t/gcd(i,j)==y)
    ans++;
    }
    cout<<ans*2<<endl;
    }

    Sol3:上面两个方法没有用到试题中的相关信息。我们用数字知识来分析下
    发现所求数对[i,j]满足一个性质,也就是
    i*j=x*y
    由于x,y是已知输入的,于是我们枚举i的话,i的值就知道了,于是j=x*y/i
    此时现在只要gcd(i,j)=x这个条件,我们就找到了所需的数字
    #include<bits/stdc++.h>
    using namespace std;
    int gcd(int a,int b){
    if(b==0)
    return a;
    return gcd(b,a%b);}
    int main(){
    int x,y,a=0;
    cin>>x>>y;
    for(int i=x;i<=y;i++)
    if((y%i==0)&&(gcd(i,x*y/i)==x))
    a++;
    cout<<a<<endl;
    return 0;
    }

    Sol4:
    我们顺着上面的思路再分析下去
    i*j=x*y
    其中i=x*i',j=x*j'
    于是x*i'*x*j'=x*y
    i'*j'=y/x
    也就是说将y/x这个数字分解成互质的两个数字即可。
    时间复杂度变为O(sqrt(N)*LogN)
    #include<bits/stdc++.h>
    using namespace std;
    int gcd(int a,int b)
    {
    if(b==0)
    return a;
    return gcd(b,a%b);
    }
    int main()
    {
    int x,y,a=0;
    cin>>x>>y;
    if (y%x!=0)
    {cout<<0<<endl;return 0;}
    else
    y=y/x;
    for(int i=1;i*i<=y;i++)
    if((y%i==0)&&(gcd(i,y/i)==1))
    a++;
    cout<<a*2<<endl;
    return 0;
    }

    Sol5:采用分解质因子的方法,时间复杂度最坏为O(sqrt(N)),最快为O(LogN)


    Sol6:采用PollardRho算法来分解大质数,O(N^0.25*LogN)

    另一个差不多的习题,也是运用Gcd,Lcm的相关性质的题

    [Noip2009]Hankson的趣味题

    Hanks博士是BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson正在思考一个有趣的问题。 今天在课堂上,老师讲解了如何求两个正整数c1和c2的最大公约数和最小公倍数。现在Hankson认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a0,a1,b0,b1,设某未知正整数x满足:

    1、 x和a0的最大公约数是a1;

    2、 x和b0的最小公倍数是b1。

    Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。

    n≤2000,1≤a,b,c,d≤2∗10^9 。

    输入

    第一行为一个正整数n,表示有n组输入数据。

    接下来的n行每行一组输入数据,

    为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。

    输入数据保证a0能被a1整除,b1能被b0整除。

    输出

    共n行。每组输入数据的输出结果占一行,为一个整数。

    对于每组数据:若不存在这样的x,请输出0;

    若存在这样的x,请输出满足条件的x的个数;

    样例输入

    2

    41 1 96 288

    95 1 37 1776

    样例输出       

    6

    2

    Hint

    第一组输入数据, x 可以是 9、 18、 36、 72、 144、 288,共有 6 个。

    第二组输入数据, x 可以是 48、 1776,共有 2 个。

    Sol1:

    易知我们所求的结果,应该是b1的约数,于是可以进行枚举,如果是从1枚举到b1

    则只能过45% 的点,程序如下

    #include<bits/stdc++.h>

    using namespace std;

    int gcd(int a,int b) { //求最大公因数

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

    }

    int main() {

        int T;

        scanf("%d",&T);

        while(T--) {

            int a0,a1,b0,b1;

            scanf("%d%d%d%d",&a0,&a1,&b0,&b1);

            int p=a0/a1,q=b1/b0,ans=0;

            for(int x=1;x<=b1;x++)

                if(b1%x==0)

                             {  //x是b1的一个因子

                    if(x%a1==0&&gcd(x/a1,p)==1)

                    //保证x与a0的最大公约数为a1

                    if (x*b0/gcd(x,b0)==b1)

                    //保证x与b0的最小公倍数为b1

                                      ans++;

                    

                           }

            printf("%d ",ans);

        }

        return 0;

    }

    改进一下,在枚举b1的约数时,只要枚举1到sqrt(b1)就好了,于是就可以全过了。

    时间复杂度为(T*Sqrt(N))

    #include<bits/stdc++.h>
    
    using namespace std;
    
    int t,a0,a1,b0,b1,ans;
    
    inline int gcd(int a,int b){if(a%b==0)return b;return gcd(b,a%b);}
    
    inline int lcm(int a,int b)
    
    {
    
    return (long long)a*b/gcd(a,b);
    
    //最好加上强制类型转化
    
    }
    
     
    
    int main()
    
    {
    
        cin>>t;
    
        while(t--)
    
        {
    
            cin>>a0>>a1>>b0>>b1;
    
            ans=0;
    
            for(int i=1;i*i<=b1;i++)
    
            {
    
                if(b1%i)continue;
    
                int j=b1/i;
    
                if(gcd(i,a0)==a1&&lcm(i,b0)==b1) ans++;
    
                if(gcd(j,a0)==a1&&lcm(j,b0)==b1&&j!=i) ans++;
    
            }
    
            cout<<ans<<endl;
    
        }
    
        
    
        return 0;
    
    }
    

      

    再优化下:针对b1进行质因子分解,统计下出现了多少类质因子,然后针对这些质因子进行dfs搜索组合下得到数字x,保证x与b0的最小公倍数为b1.然后再检查下x与a0的最大公约数是否为a1。实测出来还是比上面的跑得快,但代码量就上去了…

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define M 200005
    
    using namespace std;
    
    int prime[M],v[M],num[30][2],rp,a,b,c,d,ans,cnt = 0;
    
    void work(){
        for (int i = 2; i <= M; i++){
             if (!v[i]){
                 prime[++cnt] = i;
                 v[i] = i;
             }
             for (int j = 1; j <= cnt; j++){
                  if (prime[j] > v[i] || i * prime[j] > M) break;
                  v[i * prime[j]] = prime[j];
             } 
        }
    }
    int gcd(int aa, int bb){
        return bb ? gcd(bb, aa % bb) : aa;
    }
    
    void dfs(int x, int dep)
    {
        if (x > rp)
    	{
            if (gcd(a,dep) == b)
                  //  if (gcd(d/c,d/dep) == 1) 
                  if ((long long )dep*c/gcd(dep,c)==d)
    				    ans++;
            return;
        }
        int cp = 1;
        for (int i = 0; i <= num[x][1]; i++)
        //每个质因子最少取0个,最多取num[x][1]个 
    	{
             dfs(x + 1, dep * cp);
             cp *= num[x][0];
        }   
    }
    
    int main(){
        work();
        int T;
        scanf("%d", &T);
        while (T--){
             scanf("%d %d %d %d", &a, &b, &c, &d);
             if (d % c ==0)
    		 {
                 rp = 0;
                 int e = d;
                 for (int i = 1; i <= cnt; i++)
                 //对d进行质因子分解 
    			 {
                      if (e == 1) break;
                      int x = prime[i];
                      if (e % x == 0)
    				  {
                          num[++rp][0] = x;
                          num[rp][1] = 0;
                          while (e % x == 0)
    					  {
                                 num[rp][1]++;
                                 e /= x;
                          }
                      }
                 }
                 if (e != 1) 
    			     num[++rp][0] = e, num[rp][1] = 1;
                 ans = 0;
                 dfs(1,1);
                 //对第1个质因子开始找起 
                 printf("%d
    ", ans);
              } 
              else 
    		      printf("0
    ");
        }
        return 0;
    }
    

      

  • 相关阅读:
    111
    实验 12 综合练习二
    实验 11结构体
    作业 5 指针应用1
    实验 10 指针2
    实验9 指针1
    实验8 数组2
    实验7
    321
    实验9-2
  • 原文地址:https://www.cnblogs.com/cutemush/p/14735783.html
Copyright © 2011-2022 走看看