P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题

题目传送门

一、穷举p和q

#include <bits/stdc++.h>

using namespace std;

//最大公约数
int gcd(int x, int y) {
    return y ? gcd(y, x % y) : x;
}

//最小公倍数
int lcm(int x, int y) {
    return y / gcd(x, y) * x; //注意顺序,防止乘法爆int
}

int cnt;
int x, y;

int main() {
    //y的范围1e6,双重循环妥妥的TLE
    cin >> x >> y;
    //双重循环,傻查法,最基本,最朴素
    for (int i = x; i <= y; i++)
        for (int j = x; j <= y; j++)
            if (gcd(i, j) == x && lcm(i, j) == y) cnt++;
    cout << cnt << endl;
    return 0;
}

此暴力算法,得分\(60\),有\(4\)个点\(TLE\)。看来需要优化一下。

二、利用p计算q

因为性质4知道:\(gcd(p,q)*lcm(p,q)=p*q\),所以已知最大公约和最小公倍,枚举\(p\),就可以通过上面的等式计算获得到\(q\),这比上面的枚举\(p\)\(q\)的双重循环要减少一层循环,用信息学的行话就是时间复杂度由\(O(N^2)\)降为\(O(N)\),历史级别的进步啊!!

#include <bits/stdc++.h>

using namespace std;

//最大公约数
int gcd(int x, int y) {
    return y ? gcd(y, x % y) : x;
}

//最小公倍数
int lcm(int x, int y) {
    return y / gcd(x, y) * x; //注意顺序,防止乘法爆int
}

int cnt;
int x; //最大公约数
int y; //最小公倍数

int main() {
    //利用性质4:gcd(p,q)*lcm(p,q)=p*q
    //可以减少一维循环
    cin >> x >> y;
    for (int p = x; p <= y; p++) { //穷举进化法
        int q = x * y / p; //已知p,通过性质最大公约*最小公倍=两个数的乘积,
        // 可以计算出另一个数字,这样,就可以去掉一层循环,效率明显提升。
        // 应该满足如下的要求
        if (gcd(p, q) == x && lcm(p, q) == y) cnt++;
    }
    cout << cnt << endl;
    return 0;
}

到此,此题就已经\(AC\)了,可以说出题人还是比较良心,(≧▽≦)/ 但抓住一个好题,一定要一追到底,看看还能挖出哪些办法来。

三、穷举lcm(p,q)的约数

我们知道,\(p\)\(q\)一定是最小公倍数的约数!(这不是废话吗?)如果我们能枚举\(lcm(p,q)\)的所有约数,就肯定能碰到\(p,q\)
那怎么才能枚举\(lcm(p,q)\)的所有约数呢?我们这里采用的是从\(1\)遍历到\(\sqrt{y}\)的方法,试试\(y%k==0\),如果是的话,表示\(k\)就是\(y\)的约数。这样做,就可以找到\(y\)中所有的小因子,对应的大因子除一下就是了。这两个其实是一对一对出现的,比如\(3,60\)\(60,3\)就是两个。需要正着判断一次,反着再判断一次。同时小心类似于\(\sqrt{25}=\sqrt{5*5}\)这样的情况,去重一个。

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;

/**
 测试数据:
 3 60

 答案:4
 */
//最大公约数
LL gcd(LL x, LL y) {
    return y ? gcd(y, x % y) : x;
}

//最小公倍数
int lcm(int x, int y) {
    return y / gcd(x, y) * x; //注意顺序,防止乘法爆int
}

int cnt;
LL x; //最大公约数
LL y; //最小公倍数

int main() {
    cin >> x >> y;
    //理论依据:gcd(p,q)*lcm(p,q)=p*q
    //枚举最小公倍数y的约数,这个约数可能是p,也可能是y/p
    for (LL k = 1; k * k <= y; k++)
        if (y % k == 0) {
            //可能小因子是p,也可能大因子是p,但大小因子别一样,那样会算重了。
            LL p = k;
            LL q = x * y / k; //q是靠计算出来的。q=x*y/p
            if (gcd(p, q) == x) cnt++;

            //p=y/k,q=x*y/(y/k)=k*x 两组不能重复,就是 y/k!=k
            if (k == y / k)continue;

            //p是大因子的可能性
            p = y / k;
            q = k * x;
            if (gcd(p, q) == x) cnt++;
        }
    cout << cnt << endl;
    return 0;
}

这个计算效率就很牛了,时间复杂度:\(O(\sqrt{y})\)

四、穷举gcd(p,q)的倍数

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;

/**
 测试数据:
 3 60

 答案:4
 */
//最大公约数
LL gcd(LL x, LL y) {
    return y ? gcd(y, x % y) : x;
}

//最小公倍数
int lcm(int x, int y) {
    return y / gcd(x, y) * x; //注意顺序,防止乘法爆int
}

int cnt;
LL x; //最大公约数
LL y; //最小公倍数

int main() {
    cin >> x >> y;
    //理论依据:gcd(p,q)*lcm(p,q)=p*q
    //枚举最大公约数x的倍数
    for (LL p = x; p <= y; p += x) {
        LL q = x * y / p;
        if (gcd(p, q) == x && lcm(p, q) == y) cnt++;
    }
    cout << cnt << endl;
    return 0;
}
posted @ 2021-08-28 10:09  糖豆爸爸  阅读(432)  评论(0编辑  收藏  举报
Live2D