[Uva 11889] Benefit

题目描述

给定两个正整数\(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分解法的讨论;
  • 感谢洛谷平台的支持

写在最后

感谢大家的关注和阅读。
本文章借鉴了少许思路,最后经过本人思考独立撰写此文章,如需转载,请注明出处。

posted @ 2018-05-13 11:04  孤独·粲泽  阅读(231)  评论(3编辑  收藏  举报