有限小数(小数转换进制,判断一个数的质因子是否是另一个数质因子的子集)
题意
给定三个整数\(p\),\(q\),\(b\),请你计算十进制表示下的\(p/q\)的结果在\(b\)进制下是否为有限小数。
题目链接:https://www.acwing.com/problem/content/4487/
数据范围
\(1 \leq T \leq 10^5\)
\(0 \leq p, q, b \leq 10^{18}\)
思路
小数转换进制的方法与整数不同。整数是一直除以\(b\),直到商为\(0\)为止;而小数是一直乘\(b\),直到变为整数为止,如果一直不能变为整数,则为无限小数。
因此,对于最简分数\(p/q\),如果在二进制下它为有限小数,则存在正整数\(k\),使得\(q | b^k\)。即,\(q\)的质因子是\(b\)质因子的子集。
如果直接进行质因子分解,那么时间复杂度是根号的,会超时,因此需要思考更快的算法。
如果\(gcd(q, b) \neq 1\),那么一直做\(q = q / gcd(q, b)\)。如果循环结束后\(q \neq 1\),则说明\(q\)中存在\(b\)中不存在的质因子。这样做还是超时的,因此需要继续优化。
- 可以在每次循环的时候让\(b = gcd(b, q)\)
- 找到一个\(gcd(q, b)\),\(q\)就把这个因子给除干净
这样就可以顺利通过此题了。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
int T;
scanf("%d", &T);
while(T -- ) {
ll p, q, b;
scanf("%lld%lld%lld", &p, &q, &b);
ll d = gcd(p, q);
q /= d;
while(q > 1) {
d = gcd(q, b);
if(d == 1) break;
while(q % d == 0) q /= d, b = d;
}
if(q == 1) puts("YES");
else puts("NO");
}
return 0;
}