这一次仍然情况特殊--在外旅游,并且又不小心生了病(腹泻,高烧),实在是狼狼又狈狈,这两篇比较水的博客,后面旅游结束一定好好修改
第一题
只能说暴力极了,但是,您充值的用户已停机,永远怀念吧(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来存中间结果,避免出错。
总结步骤:
-
读a和b,边读边取模。
-
如果b是0,输出Angry!。
-
否则,用扩展欧几里得算法求b的逆元。
-
结果是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] 也是互质的,因此不符合最小互质区间的定义。
具体步骤如下:
-
检查区间 [l, r] 是否包含1,若包含则计数加1。
-
统计所有满足条件的相邻数对 [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] 中的素数个数。直接暴力判断每个数是否为素数肯定是不可行的,因为时间复杂度会爆炸。我们需要一种更高效的方法。
那咋办嘞
-
预处理小范围内的素数
先用线性筛(欧拉筛)预处理出 R 范围内的所有素数。这是因为对于任意一个合数 n,它必定有一个不大于 n 的质因数。所以,我们只需要用这些小素数去筛掉区间 [L,R] 中的合数即可。 -
区间筛法
对于区间 [L,R],用预处理的素数去标记这个区间内的合数。具体来说:-
对于每个素数 p,找到区间 [L,R] 中第一个能被 p 整除的数 start。
-
从 start 开始,每隔 p 的距离标记为合数。
-
-
特判
-
如果 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。
关键
-
分解质因数:因为 P 和 Q 的乘积等于 x0×y0,而 x0 和 y0 的质因数分解可以帮助找到所有可能的 P 和 Q。
-
互质条件:为了满足最大公约数是 x0,x0P 和 x0Q 必须互质(即它们的最大公约数为 1)。
-
对称性:P 和 Q 的顺序可以互换,所以每对 (P,Q) 和 (Q,P) 都算作不同的解。
思路
-
计算 x0 和 y0 的质因数:先分解 x0 和 y0 的质因数,然后计算它们的乘积 x0×y0。
-
枚举可能的 P 和 Q:从 x0 开始,枚举所有可能的 P 和 Q,确保它们的乘积等于 x0×y0,并且它们的最大公约数是 x0。
-
统计满足条件的对数:通过上述条件筛选出所有满足条件的 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;
}