[洛谷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
",ans);
}
return 0;
}