算术基本定理:
- 对于任意一个正整数n,都可将其分解成这样的形式:
n=p1a1 p2a2 p3a3 …pkak - 其中p1,p2,p3…pk指从2开始的素数,即2,3,5,7,11······;a1,a2,a3…ak都是自然数。
思路
- 求最大公约数和最小公倍数可以用算术基本定理求。
gcd*=pow(p[i],min(a1[i],a2[i])); lcm*=pow(p[i],max(a1[i],a2[i]));- 那么现在就是要分解质因数了。
- 要分解质因数首先得把素数都给找出来吧?
void ols(int n)
{
for(ri i=2;i<=n;++i)
{
if(!flag[i]) prime[cnt++]=i;
for(ri j=1;j<=cnt;j++)
{
if(i*prime[j]>n) break;
flag[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
for(ri i=1;i<=cnt;++i)
{
while(x%prime[i]==0)
{
a1[i]++;
x/=prime[i];
}
while(y%prime[i]==0)
{
a2[i]++;
y/=prime[i];
}
if(x==1&&y==1) break;
}
gcd*=pow(prime[i],min(a1[i],a2[i]));
lcm*=pow(prime[i],max(a1[i],a2[i]));
解题分析
题目传送门
- 题目要求满足条件的数有几个,那么我们就可以假设满足条件的数为x和y,则有
x*y=gcd*lcm,即y=gcd*lcm/x。 - 对于这道题,他给的是两个数的最大公约数和最小公倍数,那么显然满足条件条件的两个数都大于gcd,小于lcm。
- 在这里很容易想到解的个数为偶数,因为x,y可以交换,所以我们可以假设
x<y,最后答案再乘2就行。(注意这里gcd可能等于lcm,那么此时答案就为1,需要特判) - 所以我们有
gcd<=x<y<=lcm。 - 可以想象满足题意的都是gcd的倍数,所以只需要判断gcd的倍数和此时的y满不满足题意即可。
最后代码如下
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#define ri register int
#define maxn 100001
using namespace std;
int flag[maxn]={1,1};
int prime[maxn];
int a1[maxn];
int a2[maxn];
int cnt=1;
inline int read()
{
ri x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10-'0'+ch;
ch=getchar();
}
return x*f;
}
void ols(int n)
{
for(ri i=2;i<=n;++i)
{
if(!flag[i]) prime[cnt++]=i;
for(ri j=1;j<=cnt;j++)
{
if(i*prime[j]>n) break;
flag[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
int main()
{
ri x0=read(),y0=read(),ans=0;
if(x0==y0)
{
printf("1");
return 0;
}
ols(y0);
for(ri i=1;i*x0<y0;++i)
{
ri x=i*x0,y=x0*y0/x;
if(x>y) break;
memset(a1,0,sizeof(a1));
memset(a2,0,sizeof(a2));
for(ri j=1;j<=cnt;++j)
{
while(x%prime[j]==0)
{
a1[j]++;
x/=prime[j];
}
while(y%prime[j]==0)
{
a2[j]++;
y/=prime[j];
}
if(x==1&&y==1) break;
}
ri gcd=1,lcm=1;
for(ri j=1;j<=cnt;++j)
{
gcd*=pow(prime[j],min(a1[j],a2[j]));
lcm*=pow(prime[j],max(a1[j],a2[j]));
}
if(gcd==x0&&lcm==y0) ans++;
}
printf("%d",ans*2);
return 0;
}