一、单项选择题(共 15 题,每题 2 分,共 30 分)
📌 快速法:D 的二进制 1001 101 011 按三位分组 = (1153)₈ ≠ (1151)₈,一眼看出不同。
起始小写 → 第 0 轮大写、第 1 轮小写……第 20 轮为大写,第一个字母是 A。
📌 验证法:取 k = 2、h = 2 的满二叉树共 7 个结点,代入只有 A 得 7。
N 为奇数时向上取整,故选 ⌈3N/2⌉ − 2。
3 条边 2 种(链、星)、4 条边 2 种(环、带一条弦)、5 条边 1 种、6 条边 1 种(完全图),共 6 种。
互质 = 10000 − 6000 = 4000 个(即 φ(10000) = 10000 × 1/2 × 4/5)。
while (x) { ret++; ______; },空格内应填入( )。A 也能统计但要循环「位数」次;C、D 会死循环。
二、问题求解(共 2 题,每题 5 分,共 10 分)
· 由③「丙去 → 丁不去」,丙去了 ⇒ 丁没去。
· 由④「丁不去且甲不去 → 丙不去」,其逆否为「丙去 → 丁去或甲去」。已知丁没去,所以甲去了。
· 由②「乙去 → 丁去」,逆否为「丁不去 → 乙不去」⇒ 乙没去。
· 由①「下雨且乙不去 → 甲不去」,逆否为「甲去 → 不下雨或乙去」。已知甲去了、乙没去 ⇒ 没下雨。
📌 关键技巧:把蕴含式改写成逆否命题就能一路倒推。
· 0000 ~ 1999:千位只能取 0 或 1(2 种),其余三位各取 0~9 中除 8 的 9 种 → 2 × 9³ = 1458 个(含 0000,即 0)。
· 2000 ~ 2018:去掉含 8 的 2008 与 2018,剩 19 − 2 = 17 个。
不含 8 的正整数(1~2018)= 1458 − 1(去掉 0)+ 17 = 1474 个。
含 8 的数 = 2018 − 1474 = 544 个。
三、阅读程序写结果(共 4 题,每题 8 分,共 32 分)
阅读 1:大写字母后移一位易
逐个扫描字符,把大写字母的 ASCII 值加 1(A→B、G→H…),其余字符不变。
- 01#include <cstdio>
- 02char st[100];
- 03int main() {
- 04 scanf("%s", st);
- 05 for (int i = 0; st[i]; ++i) {
- 06 if ('A' <= st[i] && st[i] <= 'Z')
- 07 st[i] += 1;
- 08 }
- 09 printf("%s\n", st);
- 10 return 0;
- 11}
QuanGuoLianSai
RuanHuoMianTai
🪜 逐字符判断并后移
阅读 2:统计 i² mod x == 1 的个数中
枚举 i = 0 ~ x−1,统计满足 i² % x == 1 的 i 的个数。
- 01#include <cstdio>
- 02int main() {
- 03 int x;
- 04 scanf("%d", &x);
- 05 int res = 0;
- 06 for (int i = 0; i < x; ++i) {
- 07 if (i * i % x == 1) {
- 08 ++res;
- 09 }
- 10 }
- 11 printf("%d", res);
- 12 return 0;
- 13}
15
4
🪜 枚举 i = 0 ~ 14 检查 i² mod 15
阅读 3:二维递归 findans(表格法)难
f(n,0) = n % 3,f(0,m) = m,其余按 f(n,m) = f(n−1,m) − f(n,m−1) + f(n−1,m−1) 递推。求值时画表格逐行填最稳妥。
- 01#include <iostream>
- 02using namespace std;
- 03int n, m;
- 04int findans(int n, int m) {
- 05 if (n == 0) return m;
- 06 if (m == 0) return n % 3;
- 07 return findans(n - 1, m) - findans(n, m - 1) + findans(n - 1, m - 1);
- 08}
- 09int main(){
- 10 cin >> n >> m;
- 11 cout << findans(n, m) << endl;
- 12 return 0;
- 13}
5 6
8
🪜 造表:行 n = 0~5,列 m = 0~6
📌 递归有大量重复子问题,真实考试中直接造表(记忆化)最快。
阅读 4:置换环计数难
读入 n 与数组 d(d[i] 表示 i 指向谁),用 visited 标记沿指针走一遍,统计环的个数(等价于置换分解成循环的个数)。
- 01#include <cstdio>
- 02int n, d[100];
- 03bool v[100];
- 04int main() {
- 05 scanf("%d", &n);
- 06 for (int i = 0; i < n; ++i) {
- 07 scanf("%d", d + i);
- 08 v[i] = false;
- 09 }
- 10 int cnt = 0;
- 11 for (int i = 0; i < n; ++i) {
- 12 if (!v[i]) {
- 13 for (int j = i; !v[j]; j = d[j]) {
- 14 v[j] = true;
- 15 }
- 16 ++cnt;
- 17 }
- 18 }
- 19 printf("%d\n", cnt);
- 20 return 0;
- 21}
10 7 1 4 3 2 5 9 8 0 6
6
🪜 按 i = 0→9 依次找环(d = [7,1,4,3,2,5,9,8,0,6])
四、完善程序(共 2 题,每题 14 分,共 28 分)
完善 1:约数两两最大公约数之和(getDivisor + gcd)中
例:4 的约数 1、2、4,两两 GCD 之和 = 1 + 2 + 1 = 4。要求 getDivisor 为 O(√n),gcd 为 O(log max(a,b)),结果对 10007 取模。
- 01#include <iostream>
- 02using namespace std;
- 03const int N = 110000, P = 10007;
- 04int n;
- 05int a[N], len;
- 06int ans;
- 07void getDivisor() {
- 08 len = 0;
- 09 for (int i = 1; ① <= n; ++i)
- 10 if (n % i == 0) {
- 11 a[++len] = i;
- 12 if ( ② != i) a[++len] = n / i;
- 13 }
- 14}
- 15int gcd(int a, int b) {
- 16 if (b == 0) {
- 17 ③ ;
- 18 }
- 19 return gcd(b, ④ );
- 20}
- 21int main() {
- 22 cin >> n;
- 23 getDivisor();
- 24 ans = 0;
- 25 for (int i = 1; i <= len; ++i) {
- 26 for (int j = i + 1; j <= len; ++j) {
- 27 ans = ( ⑤ ) % P;
- 28 }
- 29 }
- 30 cout << ans << endl;
- 31 return 0;
- 32}
📋 查看各空答案与解析
第①空:i * i 只需枚举到 √n:循环条件写成 i * i <= n,复杂度 O(√n)。
第②空:n / i 避免平方数把 √n 记两次:当 n / i ≠ i 时才把大因子 n / i 也存进去。
第③空:return a 辗转相除递归出口:b == 0 时最大公约数就是 a,返回 a。
第④空:a % b 递归步:gcd(a, b) = gcd(b, a % b)。
第⑤空:ans + gcd(a[i], a[j]) 累加这一对约数的最大公约数:ans = (ans + gcd(a[i], a[j])) % P。注意括号位置——取模要作用在整个加法结果上。
🪜 以 n = 4 为例验证
完善 2:双向链表求「下一个更大值的位置」难
排列 P 已读入,a[x] = x 所在的位置。按值从小到大依次把位置从双向链表中删除;删除时它的右邻居就是「后面第一个比它大的位置」。
- 01#include <iostream>
- 02using namespace std;
- 03const int N = 100010;
- 04int n;
- 05int L[N], R[N], a[N];
- 06int main() {
- 07 cin >> n;
- 08 for (int i = 1; i <= n; ++i) {
- 09 int x;
- 10 cin >> x;
- 11 ① ;
- 12 }
- 13 for (int i = 1; i <= n; ++i) {
- 14 R[i] = ② ;
- 15 L[i] = i - 1;
- 16 }
- 17 for (int i = 1; i <= n; ++i) {
- 18 L[ ③ ] = L[a[i]];
- 19 R[L[a[i]]] = R[ ④ ];
- 20 }
- 21 for (int i = 1; i <= n; ++i) {
- 22 cout << ⑤ << " ";
- 23 }
- 24 cout << endl;
- 25 return 0;
- 26}
📋 查看各空答案与解析
第①空:a[x] = i 记录「值 x 在第 i 个位置」:a[x] = i(反查表)。后面循环 i 是「值」,a[i] 才是它的位置。
第②空:i + 1 初始化后继:R[i] = i + 1,与 L[i] = i − 1 一起构成位置 1..n 的双向链表(0 与 n+1 是天然哨兵)。
第③空:R[a[i]] 删除位置 a[i]:L[ R[a[i]] ] = L[a[i]],即让右邻居的前驱指向左邻居。
第④空:a[i] 对称的另一半:R[ L[a[i]] ] = R[a[i]],让左邻居的后继指向右邻居。注意删除后 a[i] 自己的 R 值不会被改写,正好保存了「删除那一刻的右邻居」=答案。
第⑤空:R[i] 按位置输出:位置 i 的答案就是 R[i](它在被删除的瞬间被冻结下来的右邻居);若不存在更大值则为 n + 1。