[洛谷P1072]Hankson 的趣味题「数论」
[洛谷P1072]Hankson 的趣味题「数论」
题目描述
Hanks 博士是 BT(Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson。现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数 \(c_{1}\) 和 \(c_{2}\) 的最大公约数和最小公倍数。现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数\(a_{0}\),\(a_{1}\),\(b_{0}\),\(b_{1}\) 设某未知正整数 \(x\) 满足:
1. \(x\) 和 \(a_{0}\) 的最大公约数是 \(a_{1}\);
2. \(x\) 和 \(b_{0}\) 的最小公倍数是 \(b_{1}\)。
Hankson 的“逆问题”就是求出满足条件的正整数 \(x\)。但稍加思索之后,他发现这样的 \(x\) 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的 \(x\) 的个数。请你帮助他编程求解这个问题。
输入格式
第一行为一个正整数 \(n\),表示有 \(n\) 组输入数据。接下来的\(n\) 行每行一组输入数据,为四个正整数 \(a_{0}\),\(a_{1}\),\(b_{0}\),\(b_{1}\),每两个整数之间用一个空格隔开。输入数据保证 \(a_{0}\) 能被 \(a_{1}\) 整除,\(b_{1}\) 能被 \(b_{0}\) 整除。
输出格式
共 \(n\) 行。每组输入数据的输出结果占一行,为一个整数。
对于每组数据:若不存在这样的 \(x\),请输出 \(0\),若存在这样的 \(x\),请输出满足条件的 \(x\) 的个数;
输入输出样例
输入 #1
2
41 1 96 288
95 1 37 1776
输出 #1
6
2
思路分析
- 复习数论时从虎歌博客上看到这道题,拿来做一做
- 求最大公约数和最小公倍数而已,上去从\(a_{1}\)暴力枚举,直接判断,显然超时,只拿了50分。
- 这题是在素数的唯一分解定理下推荐的,这题竟然还和素数有关系???那就考虑用一下这个定理
素数唯一分解定理:
- 定义:任何一个大于 1 的正整数都能被唯一分解为有限个素数的乘积
那么这题和这个定理到底有啥关系? - 本题应用:
首先我们可以根据这个定理得出以下关系:
\(a_{1} = p_{1}*p_{2}*...*p_{x}*p_{y}\)
根据题意:\(gcd(x,a_{0})=a_{1}\)
所以可以得出这样的关系:
\(x = p_{1}*p_{2}*p_{x}*p_{y}*...*p_{i}*p_{j} = a_{1}*...*p_{i}*p_{j}\)
\(a_{0} = p_{1}*p_{2}*p_{x}*p_{y}*...*p_{m}*p_{n} = a_{1}*...*p_{m}*p_{n}\)
不难发现,\(x\)和\(a_{0}\)除与\(a_{1}\)相同的部分素数以外,其余的各自的组成素数各不相同,即\(x/a_{1}\)与\(a_{0}/a_{1}\)互质,得出\(gcd(x/a_{1},a_{0}/a_{1})==1\) - 推广结论:
对于两个正整数\(a,b\),设\(gcd(a,b)=k\),则存在\(gcd(a/k,b/k)=1\) - 应用结论:
根据\(lcm(x,b_{0})=b_{1}\)得出\(gcd(x,b_{0}) = x*b_{0}/b_{1}\)
最后可推出以下两个式子
\(gcd(x/a_{1},a_{0}/a_{1})=1\)
\(gcd(b_{1}/b_{0},b_{1}/x)=1\)
接下来我们只要枚举\(b_{1}\)的因子,并且这个因子是\(a_{1}\)的倍数,同时满足以上两式即可
ps:挨个打数学符号是真滴麻烦
代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int gcd(int x,int y){
return y==0? x : gcd(y,x%y);
}
int lcm(int x,int y){
return x*y/gcd(x,y);
}
int main(){
int n;n = read();
for(int i = 1;i <= n;i++){
int a,a1,b,b1;
a = read(),a1 = read(),b = read(),b1 = read();
int ans = 0;
for(int x=1;x*x<=b1;x++){//枚举到sqrt(b1)即可
if(b1%x==0){
if(x%a1==0&&gcd(x/a1,a/a1)==1&&gcd(b1/b,b1/x)==1) ans++; //满足条件
int y=b1/x;//同时得出另一个因子
if(x==y) continue;
if(y%a1==0&&gcd(y/a1,a/a1)==1&&gcd(b1/b,b1/y)==1) ans++;
}
}
printf("%d\n",ans);
}
return 0;
}