备战蓝桥杯学习笔记(四)

这一次仍然情况特殊--在外旅游,并且又不小心生了病(腹泻,高烧),实在是狼狼又狈狈,这两篇比较水的博客,后面旅游结束一定好好修改

第一题

只能说暴力极了,但是,您充值的用户已停机,永远怀念吧(T_T) 🐸

首先,输入的数a和b可能非常大,直接读会爆,怎么办?这时候有个技巧:边读边取模。比如读入每一位数字的时候,直接把当前结果模上19260817,这样就能把大数变成容易处理的小数了。这一步在代码里的getint函数里实现,它会边读边模,很巧妙。

接下来,如果处理后的b是0怎么办?这时候根据题目条件,a一定不是0(因为题目保证a和b不同时是19260817的倍数),所以方程bx≡a无解,直接输出Angry!。

否则,我们需要求b的逆元。这里的关键是,模数19260817是一个质数,所以当b不为0时,它和模数一定是互质的,逆元一定存在。这时候用扩展欧几里得算法就能求出逆元x。然后答案就是a乘以x再模19260817的结果。

扩展欧几里得算法的细节可能有点难,但它的作用就是找到x和y,使得bx + 19260817y = 1。因为模数是质数,只要b不为0,这个方程一定有解。解出来的x就是我们需要的逆元。

最后注意,计算a*x的时候可能会溢出,所以用long long来存中间结果,避免出错。

总结步骤:

  1. 读a和b,边读边取模。

  2. 如果b是0,输出Angry!。

  3. 否则,用扩展欧几里得算法求b的逆元。

  4. 结果是a乘逆元再取模。

这样处理下来,问题就解决了。代码里的细节比如调整逆元为正数、处理大数输入都很关键,但思路清晰的话就迎刃而解啦。

#include <cstdio>
#include <cctype>
const int MOD = 19260817;

// 快速读入并取模
inline int getint() {
    int res = 0, ch = getchar();
    while (!isdigit(ch) && ch != EOF) ch = getchar(); // 跳过非数字
    while (isdigit(ch)) {
        res = (res << 3) + (res << 1) + (ch - '0');
        res %= MOD; // 每步都取模防止溢出
        ch = getchar();
    }
    return res;
}

int x, y; // 扩展欧几里得的解
void exgcd(int a, int b) {
    if (b == 0) {
        x = 1; y = 0;
        return;
    }
    exgcd(b, a % b);
    int tmp = x;
    x = y;
    y = tmp - a / b * y;
}

int main() {
    int a = getint();
    int b = getint();
    
    if (b == 0) { // 分母模后为0则无解
        puts("Angry!");
        return 0;
    }
    exgcd(b, MOD); // 求b的逆元
    x = (x % MOD + MOD) % MOD; // 调整x为正数
    printf("%lld\n", a * (long long)x % MOD); // 注意long long转换
    return 0;
}

第二题

思路

要解决这个问题,我们需要找出给定区间 [l, r] 中包含的所有最小互质区间的数量。最小互质区间的定义是:该区间本身互质,且不包含任何其他更小的互质子区间。

当且仅当元素为1时,区间 [1, 1] 是互质的,并且没有更小的子区间,因此它是一个最小互质区间。

相邻的两个数 x 和 x+1 总是互质的,但只有当 x ≥ 2 时,这样的区间才是有效的。因为当 x = 1 时,区间 [1, 2] 包含子区间 [1, 1],而 [1, 1] 也是互质的,因此不符合最小互质区间的定义。

具体步骤如下:

  1. 检查区间 [l, r] 是否包含1,若包含则计数加1。

  2. 统计所有满足条件的相邻数对 [x, x+1] 的数量,其中 x ≥ 2。

来看看代码

#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int l, r;
        cin >> l >> r;
        int ans = 0;
        // 检查是否存在单独的1
        if (l <= 1 && r >= 1) {
            ans += 1;
        }
        // 计算相邻数对的数量,其中x >=2
        int start = max(l, 2);
        int end = r - 1;
        if (start <= end) {
            ans += end - start + 1;
        }
        cout << ans << endl;
    }
    return 0;
}

第三题

这道题看起来挺唬人的,尤其是给的区间范围那么大,R−L≤106,R<231,感觉像是要直接把人吓跑。题目要求计算区间 [L,R] 中的素数个数。直接暴力判断每个数是否为素数肯定是不可行的,因为时间复杂度会爆炸。我们需要一种更高效的方法。

那咋办嘞

  1. 预处理小范围内的素数
    先用线性筛(欧拉筛)预处理出 R​ 范围内的所有素数。这是因为对于任意一个合数 n,它必定有一个不大于 n​ 的质因数。所以,我们只需要用这些小素数去筛掉区间 [L,R] 中的合数即可。

  2. 区间筛法
    对于区间 [L,R],用预处理的素数去标记这个区间内的合数。具体来说:

    • 对于每个素数 p,找到区间 [L,R] 中第一个能被 p 整除的数 start。

    • 从 start 开始,每隔 p 的距离标记为合数。

  3. 特判

    • 如果 L=1,需要特判,因为 1 不是素数。

    • 区间 [L,R] 的长度可能很大,但 R−L≤10e6,所以我们可以用一个布尔数组标记区间内的合数。

#include <iostream>
#include <vector>
using namespace std;

// 计算最大公约数
int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

int main() {
    int x0, y0;
    cin >> x0 >> y0;

    int product = x0 * y0; // P * Q = x0 * y0
    int count = 0;

    // 枚举所有可能的 P
    for (int P = x0; P <= product; P += x0) {
        int Q = product / P; // Q = x0 * y0 / P
        if (Q * P == product && gcd(P, Q) == x0) {
            count++;
        }
    }

    cout << count << endl;
    return 0;
}

第四题

题目要求 P 和 Q 的最大公约数(GCD)是 x0​,最小公倍数(LCM)是 y0​。换句话说,P 和 Q 都是 x0​ 的倍数,同时它们的乘积 P×Q 应该等于 x0​×y0​。

关键

  1. 分解质因数:因为 P 和 Q 的乘积等于 x0​×y0​,而 x0​ 和 y0​ 的质因数分解可以帮助找到所有可能的 P 和 Q。

  2. 互质条件:为了满足最大公约数是 x0​,x0​P​ 和 x0​Q​ 必须互质(即它们的最大公约数为 1)。

  3. 对称性:P 和 Q 的顺序可以互换,所以每对 (P,Q) 和 (Q,P) 都算作不同的解。

思路

  1. 计算 x0​ 和 y0​ 的质因数:先分解 x0​ 和 y0​ 的质因数,然后计算它们的乘积 x0​×y0​。

  2. 枚举可能的 P 和 Q:从 x0​ 开始,枚举所有可能的 P 和 Q,确保它们的乘积等于 x0​×y0​,并且它们的最大公约数是 x0​。

  3. 统计满足条件的对数:通过上述条件筛选出所有满足条件的 P 和 Q 对,并统计它们的数量。

代码

#include <iostream>
#include <vector>
using namespace std;

// 计算最大公约数
int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

int main() {
    int x0, y0;
    cin >> x0 >> y0;

    int product = x0 * y0; // P * Q = x0 * y0
    int count = 0;

    // 枚举所有可能的 P
    for (int P = x0; P <= product; P += x0) {
        int Q = product / P; // Q = x0 * y0 / P
        if (Q * P == product && gcd(P, Q) == x0) {
            count++;
        }
    }

    cout << count << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值