最大公约数与最小公倍数问题

输入二个正整数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;
}

  

原文地址:https://www.cnblogs.com/cutemush/p/14735783.html