专栏文章

LGR-213 洛谷入门赛 #31 题解(To be updated)

题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miqh5wjv
此快照首次捕获于
2025/12/04 04:44
3 个月前
此快照最后确认于
2025/12/04 04:44
3 个月前
查看原文

A 数字谜

前置芝士:表达式,位值原理,顺序结构,加减运算,(分支结构)
明显,个位以后的数都是没有必要的,因为我们只需要知道个位数字是几就可以得出答案。
知道 aa 的个位数只需要将 aa1010 取模即可,将这个数设为 pp
我们并不知道 bbpp 的大小关系。可以分类讨论:
0 & b = p \\ b - p & b > p \\ b + 10 - p & b < p \end{cases}$$ 其中 $ans$ 表示本题答案。这就是一个减法退位的原则。实际上,如果你了解取模运算,这个过程就可以直接写为 `(b - p + 10) % 10`。为什么呢?$b \geq p$ 时,其实答案就是 $b - p$,为了 $b < p$ 的情况,我们还需要加上 $10$ 以免 $b < p$,最后对 $10$ 取模也只是这个数的个位数字而已。 ```cpp #include <iostream> using namespace std; int main() { int a, b; cin >> a >> b; int p = a % 10; int ans = (b - p + 10) % 10; cout << ans << endl; return 0; } ``` ### B 会场座位 前置芝士:数组,遍历,等差数列,循环结构,间隔问题 可以明显看出来以 $1$ 和 $0$ 为分界线,左边的是一个首项为 $99$,公差为 $-2$,末项为 $1$ 的递减等差数列,而右边的是一个首项为 $0$,公差为 $2$,末项为 $98$ 的递增等差数列。 把座位号存储在数组 $a$ 中即可。当然,不能傻傻地打表,因为有规律,用 for 循环。 可我们都不知道这两个数列的项数,很简单,等差数列项数公式就是首项减末项的差除以公差再加一的和。简而言之,就是 $\frac{a - b}{x} + 1$,其中 $a,b,x$ 分别代表首项、末项和公差。 算出来两个等差数列的项数都是 $50$。 用两个 for 循环存储即可。当然,也有用一个 for 循环就可以存储的方法,将在代码中给出。 然后找到两个座位号的下表 $pos_a,pos_b$,相减即可。 错!两个座位号的位置可能是 $pos_a < pos_b$ 或 $pos_a > pos_b$,要用绝对值。还有,因为我们要知道的是两个座位的间隔而不是距离,所以我们还要减 $1$。 实际上,可以用 STL 中的 find 函数来找这个位置的下标。~~但对于入门赛来说,这不值得。~~ ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; vector<int> n(100); int main() { for (int i = 0; i < 50; ++i) { n[i] = 99 - 2 * i; n[50 + i] = 2 * i; } int a, b; cin >> a >> b; int pos_a = find(n.begin(), n.end(), a) - n.begin(); // 这里的 find 函数涉及到了数组的迭代器的偏移量,将在后面的二分也会学习到同样的做法。 int pos_b = find(n.begin(), n.end(), b) - n.begin(); int gap = abs(pos_a - pos_b) - 1; cout << gap << endl; return 0; } ``` 其中 abs 函数是求绝对值,在 algorithm 头文件可以找到。 find 函数等价于: ```cpp for (int i = 1; i <= 100; i++) { if (a == n[i]) pos_a = i; if (b == n[i]) pos_b = i; } ``` ### C Pollard-Rho 前置芝士:表达式,循环结构 只需要在每一次开锁后改密码即可。 但是!但是!但是! 第一次开门的密码是原始密码,开门以后才改密码,第二次开门的密码是修改一次的密码。 简而言之,第 $n$ 次开门的密码是修改 $(n-1)$ 次的密码($n > 0$,$n-1=0$ 时表示原始密码)。 for 循环,注意循环结束的条件是 $i \le k-1$,也就是 $i < k$。 ```cpp #include <iostream> using namespace std; int main() { int x1, C, k; cin >> x1 >> C >> k; int x = x1; for (int i = 1; i < k; ++i) x = (x * x + C) % 10000; cout << x << endl; return 0; } ``` ### D 检票 前置芝士:分支结构,数组 ~~这 $1000$ 分拿至少 $900$ 不就是有手……~~ 开一个数组存储原来的队列,遍历寻找小于等于 $15$ 分钟的人,注意不能排序,从下标 $0$ 到 $n-1$,找到一个小于等于 $15$ 分钟的人就把他往第二个数组里推,注意要累加下标;大于 $15$ 分钟就把他往第三个数组里推,注意也要累加下标。最后先输出第二个数组再输出第三个数组即可。 ```cpp #include <iostream> #include <vector> using namespace std; vector<int> t; vector<int> u, nu; int main() { int n; cin >> n; for (int i = 0; i < n; ++i) cin >> t[i]; for (int i = 0; i < n; ++i) { if (t[i] <= 15) u.push_back(t[i]); else nu.push_back(t[i]); } for (int i = 0; i < u.size(); ++i) cout << u[i] << " "; for (int i = 0; i < nu.size(); ++i) cout << nu[i] << " "; return 0; } ``` ### E 右箭头 前置芝士:数学归纳,循环结构,轴对称,数组 倒数第二个 A 的。 可能我的做法并不是最优解,但可以参考。 注意到 $n,k$ 均为奇数,所以我们可以把右箭头分成以下三个部分:(以样例 $1$ 为例) ``` ......#... ......##.. #########. Part 1 ########## Part 2 #########. ......##.. ......#... Part 3 ``` 当然,样例 $2$ 和 $3$ 这些特殊情况也可以这么分。 Part 2 就是长为 $m$ 的井号长条,Part 3 就是 Part 1 的对称版,所以我们只需要知道 Part 1 怎么搞即可。 首先,Part 1 可以继续细分为: ``` ......#... ......##.. Part 1.1 #########. Part 1.2 ``` 而 Part 1.1 也可以继续细分为: ``` ...... #... ...... ##.. Part 1.1.1 Part 1.1.2 ``` Part 1.1.1 只需要把整个 Part 1 数组全部设为 `.` 即可。 Part 1.1.2 明显就是一个直角三角形。但是我们不知道的东西太多了。 首先,Part 1.1.2 在 Part 1 数组的位置在哪里? 这就和箭头的直角三角形的斜边的高有关系了。 样例 $3$ 就是样例 $1$ 的箭头的直角三角形。可以看见,

评论

0 条评论,欢迎与作者交流。

正在加载评论...