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