博主主页:Yu·仙笙
专栏地址:洛谷千题详解
目录
题目描述
输入格式
输出格式
输入输出样例
解析:
C++源码:
C++源码2:
C++源码3:
--------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------
题目描述
输入两个正整数x0,y0,求出满足下列条件的 P, Q 的个数:
-
P,Q 是正整数。
-
要求 P, Q 以x0 为最大公约数,以 y0 为最小公倍数。
试求:满足条件的所有可能的 P, Q 的个数。
--------------------------------------------------------------------------------------------------------------------------------
输入格式
一行两个正整数x0,y0。
--------------------------------------------------------------------------------------------------------------------------------
输出格式
一行一个数,表示求出满足条件的 P, Q 的个数。
--------------------------------------------------------------------------------------------------------------------------------
输入输出样例
输入 #1复制
3 60
输出 #1复制
4
--------------------------------------------------------------------------------------------------------------------------------
解析:
upd on 2020/06/12:修改了部分解释不够清楚和有歧义的地方。
upd on 2022/07/21:添加了 Latex。
前置知识
- 最大公约数(即 \gcdgcd) 和最小公倍数(即 \operatorname{lcm}lcm)的求法。
该题的关键点在于,两个数的积等于它们最大公约数和它们最小公倍数的积。公式表示为 a\times b=\gcd(a,b) \times \operatorname{lcm}(a,b)a×b=gcd(a,b)×lcm(a,b)。设作为答案的两个数为 xx 和 yy,我们要使它们同时满足以下三个条件,并统计这样的 xx 和 yy 的个数(P,QP,Q 含义见题目描述):
- x \times y=P \times Qx×y=P×Q
- \gcd(x,y)=Pgcd(x,y)=P
- \operatorname{lcm}(x,y)=Qlcm(x,y)=Q
我们可以枚举 xx,判断是否存在满足条件 11 的整数 yy(即,xx 能否被 P,QP,Q 的积整除)。满足第一个条件后,再分别判断当前的 x,yx,y 是否能够同时满足另外两个条件即可。显然,这种做法会超时。
考虑优化这个程序。我们其实并不需要枚举两次,因为对于不同的 x,yx,y ,交换它们的值一定可以得到另一组与之对应的解。因此,从 11 到 \sqrt{P\times Q}P×Q 枚举一遍,每发现一组答案就将 ansans 的值加上 22 即可。
一组 x,yx,y 有对应解时有条件:x,yx,y 的值不同。如果它们相同,交换后并不能得到与之对应的另一组数。当 x=yx=y 时,易得 x=y=\gcd(x,y)=\operatorname{lcm}(x,y)x=y=gcd(x,y)=lcm(x,y)。 所以要对此进行特判,若 P,QP,Q 相等,这种情况就存在, ansans 里要减去 11。
一些代码实现技巧:
-
c++ 里有一个自带的求 \gcdgcd 的函数叫
__gcd
。upd:现在 NOIP 已经可以使用了。 -
当积相同且 \gcdgcd 相同时,\operatorname{lcm}lcm 也一定相同,因此只需判断是否满足一、二两个条件即可。
-------------------------------------------------------------------------------------------------------------------------------
C++源码:
#include<iostream>
using namespace std;
int ans=0;//ans是情况数量。
int d(int a,int b){//求最大公约数函数。a,b是两个待求最大公约数的数,不是题里的x0y0。
if(a<b) swap(a,b);//确保a>b,为下一步除法做准备
if(a%b==0) return b;//如果a/b余0,那么b就是最大公约数。比如10和5, 10/5余0,5就是最大公约数
else return d(b,a%b);//辗转相除法求最大公因数(其实交换ab那一步没必要,小除以大余小本身,到这里自然就换过来了,比如3和15,3/15余3,到这里就变成了15和3,嗯)
}
int x(int a,int b){//最小公倍数
return (a*b/d(a,b));//最大公因数和最小公倍数的乘积等于原两个数的乘积。所以这两个数的乘积除以最大公因数等于最小公倍数。
}
int main(){
int x0,y0;//这才是x0y0.
cin>>x0>>y0;//(这里用个搜索就行了)
for(int i=x0;i<=y0;i++){//a一定在它的最大公因数和最小公倍数之间,这很明显
int j=x0*y0/i;//最大公因数和最小公倍数的乘积等于原两个数的乘积。老道理 。b就这样算
if(d(i,j)==x0&&x(i,j)==y0)// 如果ab(ij)的最大公因数,最小公倍数符合x0y0
ans++;//计数
}
cout<<ans<<endl;
return 0;
}
-------------------------------------------------------------------------------------------------------------------------------
C++源码2:
#include <bits/stdc++.h>
using namespace std;
long long x,y;
inline long long gcd(long long x,long long y)
{
if(y==0) return x;
return gcd(y,x%y);
}
int main()
{
cin>>x>>y;
long long ans=0;
for(long long i=1;i<=sqrt(x*y);i++)
{
if(x*y%i==0&&gcd(i,x*y/i)==x) ans++;
}
ans*=2;
if(x==y) ans--;
cout<<ans;
return 0;
}
-------------------------------------------------------------------------------------------------------------------------------
C++源码3:
#include <iostream>
int main()
{
int x, y;
std::cin >> x >> y;
if (y % x != 0)
std::cout << 0;
else
{
int quotient = y / x;
int count = 0; //统计素因数的个数
int currentFactor = 2; //用来试验整除性的因数
while (quotient > 1) //等于1时标志着分解完毕
{
if (quotient % currentFactor == 0)
{
count++;
while (quotient % currentFactor == 0)
quotient /= currentFactor;//若能整除就除到底
}
currentFactor++;
}
std::cout << (1 << count);//使用位运算来产生2的方幂
}
return 0;
}