题目描述
给定两个正整数\(A和C\),若存在\(B\),满足\(lcm(A,B)=C\),则输出满足条件的最小的\(B\),若不存在,则输出NO SOLUTION
。
分段得分、强化数据版本在此处:强化版题目,这里无解输出\(-1\)。
题目解答
无解情况
首先分析不存在的情况,既然\(C\)是最小公倍数了,是倍数,那么必定有\(A|C\),所以首先判断\(CmodA\),无解情况就弄完了。
40分,暴力分
首先写好欧几里得算法,也就是辗转相除法求最大公因数。又根据公式\(\frac {AB} {gcd(A,B)} = lcm(A,B)\),从\(1\)枚举到\(C\)来求\(B\)。
代码如下:
#include<cstdio>
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int main()
{
int a, c, t, j, b, d;
scanf("%d", &t);
for(int i = 1; i <= t; i++)
{
scanf("%d%d", &a, &c);
if(c % a)
j = -1;
else
for(j = 1; j <= c ; j++)
{
int r = a * j / gcd(a,j);
if(r == c)
break;
}
printf("%d\n", j);
}
return 0;
}
可能是80分的妙法
是别人在做题的时候想到的一种方法(虽然没有尝试)。
初始化处理好\(10^7\)以内的数的最小因数来加速运算(用欧拉筛法,\(O(n)\)预处理),每次询问就是\(O(log _{2} n)\)。
100分完美做法
注意到\(\frac {AB} {gcd(A,B)} = C\),则有:
\[\frac {B} {gcd(A,B)} = \frac {C} {A}\]
然后就有一个重要的东西:
若\(A \perp B\),则此时的\(B\)为最小的\(B\)。证明很简单,按照上面的公式套就行了。
如果\(A和B\)有公共部分呢?
很简单,如果有公共部分时,\(B\)绝对不会满足条件,那么解决办法就是扩大\(B\)。注意到此时的\(B和A\)乘积恒为\(C\),扩大\(B\)的办法就是将\(A\)中与\(B\)重复的部分转给\(B\),直到\(A \perp B\)为止。因为这是恰好将要求的\(B\)补满了,所以这是最小的解。代码如下:
#include <cstdio>
int gcd(int n, int m)
{
return (n = n % m) ? gcd(m, n) : m;
}
inline int solve(int a, int b)
{
int r;
while((r = gcd(a, b)) > 1)
a /= r, b *= r;
return b;
}
int main()
{
int T, A, C;
scanf("%d", &T);
for(register int i = 0; i < T; i++)
scanf("%d%d", &A, &C), C % A ? printf("-1\n") : printf("%d\n", solve(A, C / A));
return 0;
}
时间复杂度大约为\(O(Tlog \, A)\),洛谷上测试的最大耗时测试点耗时为756ms。
读入输出优化后如下:
#include <cstdio>
int gcd(int n, int m)
{
return (n = n % m) ? gcd(m, n) : m;
}
inline int solve(int a, int b)
{
int r;
while((r = gcd(a, b)) > 1)
a /= r, b *= r;
return b;
}
inline int read()
{
int x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
ch = getchar();
while(ch >= '0' && ch <= '9')
x = x * 10 + ch - '0', ch = getchar();
return x;
}
inline void Putout(int x)
{
int i = 0, a[10];
if(x)
{
while(x)
a[i++] = x % 10, x /= 10;
while(i--)
putchar(a[i]+'0');
}
else
putchar('-'), putchar('1');
putchar('\n');
}
int main()
{
int T, A, C;
T = read();
for(register int i = 0; i < T; i++)
A = read(), C = read(), C % A ? Putout(0) : Putout(solve(A, C / A));
return 0;
}
最终最大耗时测试点耗时344ms。
特别感谢
- 感谢洛谷ID为曦月__OFN的dalao对我的支持和帮助(提出强化数据建议和方案);
- 感谢洛谷ID为Twilight_的dalao提出的80分解法(本来卡一卡可以过);
- 感谢昵称为阮行止的dalao对80分解法的讨论;
- 感谢洛谷平台的支持
写在最后
感谢大家的关注和阅读。
本文章借鉴了少许思路,最后经过本人思考独立撰写此文章,如需转载,请注明出处。