2026 年 6 月 GESP C++ 五级真题 · 逐题详解
一、单选题(每题 2 分,共 30 分)
参考答案:B C A B A B C C C B B B A C A
第 1 题
假设 head != nullptr,下面是实现单向循环链表在头节点后插入新节点的代码,横线处应填入( )。
structNode {int val; Node* next;};voidinsertAfterHead(Node* head, int x){ Node* newNode = new Node; newNode->val = x; ______________________ // 在此处填入代码}A. newNode->next = head;head->next = newNode;B. newNode->next = head->next;head->next = newNode;C. head->next = newNode;newNode->next = head->next;D. newNode->next = head->next;head = newNode;
答案:B
要在 head 之后插入新节点,必须先让新节点"接上"head 原来指向的下一个节点(newNode->next = head->next),再把 head 的 next 改为指向新节点(head->next = newNode)。顺序不能颠倒:如果先执行 head->next = newNode,那么 head 原来的下一个节点信息就丢失了,此时再执行 newNode->next = head->next 只会让 newNode 指向自己,造成链表断裂。选 B。A 完全没有用到"头节点后"的语义(这是往前插的写法且逻辑本身也是错的);C 顺序颠倒,会导致数据丢失;D 里对 head 本身赋值并不会改变调用者手里的头指针(head 是按值传递的形参),且逻辑上也不符合"在头节点后插入"的要求。
考点:链表插入操作中,必须先保存/连接原有的后继指针,再修改前驱的指针,顺序错误会导致链表断裂或数据丢失。
第 2 题
下面代码遍历并输出一个循环单链表,其中 head 指向链表的第一个节点,横线处应填入的是( )。
voidprintList(Node* head){if (head == nullptr) return; Node* p = head; _______________________ // 在此处填入代码cout << endl;}A. while (p != nullptr) {cout << p->val << " "; p = p->next;}B. while (p->next != nullptr) {cout << p->val << " "; p = p->next;}C. do {cout << p->val << " "; p = p->next;} while (p != head);D. for (; p; p = p->next) {cout << p->val << " ";}
答案:C
对于循环单链表,最后一个节点的 next 指向的是 head 而不是 nullptr,所以 A、B、D 中依赖"遇到 nullptr 才停止"的写法都会陷入死循环(因为 next 永远不为空)。正确做法是用 do-while:先无条件输出并前进一步,再判断 p 是否又转回了 head,一旦回到起点就停止——这正是遍历循环链表的标准写法。选 C。
考点:循环链表的终止条件不能用"是否为空",而要用"是否回到起点"。
第 3 题
双链表结点定义如下,若要删除双链表中的中间结点(非首尾节点)p,下面写法正确的是( )。
structNode {int val; Node* prev; Node* next;};A. p->prev->next = p->next;p->next->prev = p->prev;delete p;B. p->next->prev = p->next;p->prev->next = p->prev;delete p;C. p->prev = p->next;p->next = p->prev;delete p;D. p->next->next = p->prev;p->prev->prev = p->next;delete p;
答案:A
删除中间节点 p 的核心是让 p 的前驱和后继"绕过" p 直接相连:p 的前驱节点的 next 要指向 p 的后继(p->prev->next = p->next),p 的后继节点的 prev 要指向 p 的前驱(p->next->prev = p->prev),最后再释放 p 本身。选 A。B、D 把指针关系写反或写错,会导致链表断裂甚至野指针;C 只是改动了 p 自身的指针(对已经要删除的节点做修改毫无意义),完全没有动周围节点的连接,起不到"删除"效果。
考点:双链表删除节点要同时更新前驱和后继两个方向的指针,且要注意到底是"谁的 next/prev 指向谁",这是最容易写反的地方。
第 4 题
使用如下欧几里得算法求 gcd(105, 45) 时,函数 gcd(a, b) 的递归调用序列正确的是( )。
intgcd(int a, int b){return b == 0 ? a : gcd(b, a % b);}A. gcd(105, 45) -> gcd(45, 60) -> gcd(60, 15) -> gcd(15, 0) B. gcd(105, 45) -> gcd(45, 15) -> gcd(15, 0) C. gcd(105, 45) -> gcd(60, 45) -> gcd(15, 45) D. gcd(105, 45) -> gcd(15, 45) -> gcd(15, 0)
答案:B
按递归定义逐步展开:gcd(105, 45),因为 45≠0,所以调用 gcd(45, 105%45);105%45=15,所以下一步是 gcd(45, 15)。再展开:gcd(45,15),45%15=0,所以调用 gcd(15, 0)。到此 b=0,返回 a=15,递归结束。整个序列为 gcd(105,45) -> gcd(45,15) -> gcd(15,0)。选 B。
考点:欧几里得算法每一步把 (a,b) 变成 (b, a%b),要熟练心算取模运算,避免把参数顺序或余数算错。
第 5 题
下面代码实现线性筛(欧拉筛),以筛选出 n 以内的所有素数。横线处的代码应为( )。
for (int i = 2; i <= n; ++i) {if (is_prime[i]) { primes.push_back(i); }for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) { is_prime[i * primes[j]] = false;if (________________) break; // 在此处填入代码 }}A. i % primes[j] == 0B. primes[j] % i == 0C. i % primes[j] != 0D. i == primes[j]
答案:A
线性筛的核心保证是"每个合数只被它的最小质因子筛掉一次"。当枚举到 primes[j] 恰好是 i 的一个因子(即 i % primes[j] == 0)时,说明 primes[j] 就是 i 的最小质因子,此时必须立刻 break,否则继续用更大的质数去乘 i 会导致同一个合数被重复标记,线性筛就退化成了普通筛法,丧失了 O(n) 的复杂度优势。选 A。
考点:线性筛"break 条件"是保证每个合数只被标记一次、达到 O(n) 复杂度的关键所在,是欧拉筛区别于埃氏筛的核心技巧。
第 6 题
下面关于埃氏筛法的说法正确的是( )。
A. 每个合数只会被筛掉一次 B. 从每个素数出发,把它的倍数标记为合数 C. 只能判断一个数是不是偶数 D. 不能求出素数表
答案:B
埃氏筛法的基本思想正是"从每一个素数出发,把它所有的倍数都标记为合数",选 B。A 错在埃氏筛并不保证每个合数只被筛一次(例如 12 会同时被 2 和 3 筛到,被标记多次,这也是它比欧拉筛慢的原因);C、D 都不符合事实,埃氏筛本身就是用来求任意范围内完整素数表的经典算法,不局限于判断偶数。选 B。
第 7 题
下面代码实现了计算 x^n 的快速幂算法,该算法体现的编程思想是( )。
longlongpower(longlong x, int n){if (n == 0) return1;longlong res = power(x, n / 2);if (n % 2 == 0) return res * res;elsereturn res * res * x;}A. 枚举 B. 贪心 C. 分治 D. 模拟
答案:C
这段代码把求 x^n 的问题拆解为先求出规模减半的子问题 x^(n/2),再根据 n 的奇偶性,用子问题的结果拼出原问题的答案(偶数直接平方,奇数再多乘一个 x)——"大问题拆成规模更小、结构相同的子问题,分别求解后再合并"正是分治法的典型特征,选 C。这道题把时间复杂度从朴素的 O(n) 降到了 O(log n)。
第 8 题
下面代码用于统计 n 中因子 2 出现了多少次。若 n = 40,输出是( )。
int n = 40;int cnt = 0;while (n % 2 == 0) { cnt++; n /= 2;}cout << cnt;A. 1 B. 2 C. 3 D. 4
答案:C
逐步模拟:40%2==0,cnt=1,n=20;20%2==0,cnt=2,n=10;10%2==0,cnt=3,n=5;5%2!=0,循环结束。最终 cnt=3。选 C。
考点:这是分解质因数中"提取某个质因子出现次数"的标准写法,本质是不断整除直到不能再整除为止。
第 9 题
在一个有序数组中查找第一个大于或等于 x 的元素位置,横线处应填写( )。
intlowerBound(vector<int>& a, int x){int l = 0, r = a.size();while (l < r) {int mid = l + (r - l) / 2;if (a[mid] >= x) ________________; // 在此处填入代码else l = mid + 1; }return l;}A. r = mid + 1B. r = mid - 1C. r = midD. l = mid
答案:C
这是标准的 lower_bound 写法:当 a[mid] >= x 时,说明 mid 位置有可能就是答案(或者答案在更左边),所以要把右边界收缩到 r = mid(保留 mid 这个位置继续参与后续比较,而不是直接排除掉),当 a[mid] < x 时,说明答案一定在 mid 右边,所以 l = mid + 1。选 C。如果写成 r = mid - 1(B)或 r = mid + 1(A)都会直接排除掉本应保留的候选位置,导致结果不正确;D 的 l = mid 在这个分支下逻辑也是反的。
考点:二分查找里,需要保留候选答案的一侧要写 mid 本身,需要排除掉当前 mid 的一侧才能写 mid±1,这是最容易出错的边界细节。
第 10 题
有若干根木头,长度存于 wood。每切一刀可以把一段木头分成两段。函数 check(wood, K, x) 返回:用不超过 K 刀,能否使所有木段长度都不超过 x。下面代码使用二分答案查找最小可行的 x,横线处应填( )。
intbinary_cut(vector<int>& wood, int K){int l = 1;int r = 0;for (int len : wood) r = max(r, len);while (l < r) {int mid = l + (r - l) / 2;if (check(wood, K, mid)) ________________; // 在此处填入代码else l = mid + 1; }return l;}A. r = mid + 1B. r = midC. l = midD. r = mid - 1
答案:B
check(wood, K, x) 具有单调性:x 越大,需要切的刀数越少,越容易在 K 刀内达成。题目要找的是"最小可行的 x",因此当 check(wood,K,mid) 为真时,说明 mid 有可能就是答案(或者更小的 x 也可行),要把右边界收缩到 r = mid(保留 mid 继续参与比较);当 check 为假时,说明 mid 太小了,答案一定比 mid 大,所以 l = mid + 1。这与第 9 题的 lowerBound 写法是同一套"二分答案"模板。选 B。
第 11 题
下面代码段实现了快速排序的划分操作(以首元素为基准),横线处代码应填入( )。
intpartition(vector<int>& arr, int low, int high){int pivot = arr[low];int i = low, j = high;while (i < j) {while (i < j && arr[j] >= pivot) j--;while (i < j && arr[i] <= pivot) i++;if (i < j) swap(arr[i], arr[j]); } ________________; // 在此处填入代码return i;}A. swap(arr[low], arr[high])B. swap(arr[low], arr[i])C. swap(arr[i], arr[high])D. arr[i] = pivot
答案:B
整个 while 循环结束后,i 和 j 相遇的位置就是基准 pivot(也就是 arr[low] 原来保存的值)最终应该落脚的地方;但目前 arr[low] 这个位置上仍然保留着原始的 pivot 值(因为循环体从未真正移动过 low 位置的数据,只是拿它的值做了比较)。所以最后需要把 arr[low] 和 arr[i] 交换一次,把 pivot 真正放到 i 这个正确的位置上,使得 i 左边都 <= pivot、右边都 >= pivot。选 B。D 直接赋值会覆盖掉 arr[i] 原来的值,导致该元素丢失,是错误写法。
第 12 题
下面哪句话最符合归并排序的思想?( )
A. 每次选择最小元素放到前面 B. 将数组分成两半分别排序,再合并两个有序部分 C. 相邻元素两两交换 D. 从左到右把元素插入有序区
答案:B
A 描述的是选择排序,C 描述的是冒泡排序,D 描述的是插入排序,只有 B 准确概括了归并排序"分而治之、再合并"的核心步骤。选 B。
第 13 题
在对长度为 n(n≥1)的数组进行归并排序的过程中,mergeArray 函数(合并两个有序子数组的操作)被调用的次数是( )。
A. n-1 B. log n C. n log n D. 2n
答案:A
mergeSort 的递归结构可以看成一棵二叉树:每次把区间从中点 mid 一分为二,分别递归左右两半,只有当 left < right(即区间长度大于 1)时才会在递归返回后调用一次 mergeArray。这棵递归树最终会产生 n 个"叶子"(每个叶子对应长度为 1 的子区间,不再触发 mergeArray),而对于一棵有 n 个叶子的二叉树,其内部节点(也就是发生真正合并调用的节点)的数量恰好是 n-1 个。所以 mergeArray 总共被调用 n-1 次,与树的深度(log n)无关,只与元素总数有关。选 A。
考点:不要把"每层调用一次的规律"和"总调用次数"搞混——归并排序每一层所有 mergeArray 加起来处理的元素总数是 O(n),一共 log n 层,所以总时间是 O(n log n),但 mergeArray 这个函数被"调用"的次数(不是处理的元素次数)是 n-1,这是本题容易踩坑的地方。
第 14 题
小杨在学校义卖会上负责打包"零食盲盒"。每个盲盒重量不同,快递盒最多承重 limit 克,每个快递盒最多装两个盲盒。为了尽量少用快递盒,他采用如下策略:
每次把最轻的盲盒和最重的盲盒尝试放在一起; 如果两者重量之和不超过 limit,就一起装; 否则,只能让最重的盲盒单独装一盒。
下面代码用于计算最少需要多少个快递盒,则横线处应填入的是( )。
intminBoxes(vector<int>& w, int limit){ sort(w.begin(), w.end());int l = 0, r = w.size() - 1;int boxes = 0;while (l <= r) {if (w[l] + w[r] <= limit) { __________; // 在此处填入代码 } else { r--; } boxes++; }return boxes;}A. l++;B. r--;C. l++;r--;D. boxes--;
答案:C
数组已按重量从小到大排序,l 指向当前最轻的盲盒,r 指向当前最重的盲盒。如果 w[l]+w[r] <= limit,说明这两个盲盒可以装进同一个快递盒,此时最轻和最重的盲盒都被"消耗"掉了,所以需要同时把 l 向右移、r 向左移(即 l++; r--;),并统计一个快递盒。如果超重,则只能让最重的盲盒单独装一盒,这时只消耗了 r 这一个盲盒,所以只需要 r--(题目中 else 分支已经给出)。选 C。这是经典的"双指针 + 排序"贪心模型(与"船的最大载重"一类问题同构)。
第 15 题
高精度减法中,假设两个高精度数按低位在前存储,且已经保证被减数不小于减数。下面处理借位逻辑代码中横线处应填写( )。
if (a[i] < b[i]) { a[i + 1]--; ________________;}t = a[i] - b[i];A. a[i] += 10B. a[i] -= 10C. b[i] += 10D. a[i] = a[i+1] + 10
答案:A
当当前位 a[i] 小于 b[i] 时,说明这一位不够减,需要向高位借 1(所以高位 a[i+1] 要减 1),同时当前位要加上"借来的 10",变成 a[i]+10 之后再减 b[i],才能得到正确的结果。所以在向高位借位之后,紧接着要把当前位补上 10,即 a[i] += 10。选 A。
考点:高精度减法的借位处理和竖式减法的手工计算完全一致——借位是"高位减1,低位加10",顺序和加减符号都不能写反。
二、判断题(每题 2 分,共 20 分)
参考答案:✓ ✗ ✓ ✓ ✓ ✗ ✗ ✗ ✓ ✗
第 1 题
数组的存储空间在物理上通常是连续的,而链表的结点可以存储在不连续的内存空间中。
✓ 数组元素在内存中是紧密排列、地址连续的,可以通过下标直接计算偏移量访问;链表的每个结点是单独动态分配的,彼此之间靠指针连接,物理地址完全可以是分散、不连续的。表述正确。
第 2 题
带哨兵头尾节点的双向循环链表,在表头插入节点 p,以下四步操作无论什么顺序执行结果都正确。
① p->next = head->next;② p->prev = head;③ head->next->prev = p;④ head->next = p;✗ 这四步之间存在严格的先后依赖:③ 语句中的 head->next 必须是"插入前"的原始后继节点,一旦提前执行了 ④(head->next = p),此时 head->next 已经变成了 p 自己,再执行 ③ 就变成了 p->prev = p,形成自我指向的错误连接,链表结构被破坏。因此必须先完成 ①③(利用原始的 head->next 建立/更新旧后继节点的连接),再执行 ④ 把 head->next 改指向 p,顺序不能随意调换。表述错误。
第 3 题
对任意正整数 a、b,以下两种写法的 gcd 函数返回值完全相同。
intgcd1(int a, int b){return b ? gcd1(b, a % b) : a;}intgcd2(int a, int b){while (b) {int t = b; b = a % b; a = t; }return a;}✓ 两种写法都是欧几里得算法的实现,一个用递归表达"(a,b) -> (b, a%b) 直到 b=0",一个用循环表达完全相同的状态转移过程,对任意正整数输入两者计算过程等价,返回值必然完全相同。表述正确。
第 4 题
在归并排序的合并操作中,如下代码片段可以正确地将两个已排序的子数组 L 和 R 合并回原数组 arr 中。
✓ 代码先把 [left, mid] 和 [mid+1, right] 两段分别拷贝进临时数组 L、R,然后用双指针 i、j 比较 L[i] 和 R[j],谁小就先取谁写回 arr,比较结束后再把某一侧剩余的元素依次搬运回 arr,整个逻辑是归并排序"合并"步骤的标准写法,没有遗漏或越界问题。表述正确。
第 5 题
分治法通常将一个规模较大的问题拆分为若干个规模较小、结构相似的子问题,分别求解后再合并子问题的结果。
✓ 这正是分治法(Divide and Conquer)的标准定义:分解 → 解决子问题(通常递归)→ 合并子问题的解得到原问题的解。表述正确。
第 6 题
贪心算法只要每一步选择当前最优解,就一定能得到全局最优解。
✗ 贪心算法能否得到全局最优解,取决于问题本身是否具备"贪心选择性质"和"最优子结构",并不是所有问题只要每步选择局部最优就能保证全局最优(例如经典的 0/1 背包问题用贪心就无法保证得到最优解,需要用动态规划)。题目把贪心算法的适用范围说得过于绝对,表述错误。
第 7 题
二分查找不仅可以应用于有序数组,也可以在不增加时间复杂度的情况下应用于有序的单链表,因为链表也支持 O(1) 时间内的随机访问。
✗ 单链表只能从头节点开始沿着 next 指针依次访问,不支持像数组那样通过下标直接一步跳到任意位置,也就是说链表不具备 O(1) 的随机访问能力。如果在单链表上做二分查找,每次定位"中间节点"都需要从头遍历 O(n) 步,这会让整体复杂度退化,无法保持 O(log n) 的优势。表述错误。
第 8 题
以下函数 f1 的时间复杂度比函数 f2 的更高。
voidf1(int n){for (int i = 1; i < n; i *= 2);}voidf2(int n){if (n <= 1) return; f2(n - 1); f2(n - 1);}✗ f1 中 i 每次乘以 2,循环次数约为 log₂n,时间复杂度是 O(log n);f2 每次调用自身两次、规模只减 1,属于经典的指数级递归,时间复杂度是 O(2ⁿ)。O(2ⁿ) 远远高于 O(log n),题目把两者高低关系说反了。表述错误。
第 9 题
唯一分解定理表明,任何一个大于 1 的自然数都可以唯一地分解为若干个质数的乘积,如果不考虑质因数的顺序,这种分解方式是唯一的。
✓ 这是算术基本定理(唯一分解定理)的标准表述:每个大于 1 的整数都能写成质因数的乘积,且这种分解在不计次序的情况下是唯一的。表述正确。
第 10 题
归并排序和快速排序在平均情况下的时间复杂度均为 O(n log n)。但在稳定性方面,归并排序通常是不稳定的,而快速排序是稳定的。
✗ 前半句关于平均时间复杂度的表述是对的,但后半句把两者的稳定性说反了:归并排序在合并时只要保证"相等元素优先取左边(或按序取)",天然就是稳定的排序算法;而快速排序在划分过程中会发生远距离的元素交换,很容易跨过相等的元素,通常被认为是不稳定的排序算法。表述错误。
三、编程题(每题 25 分,共 50 分)
3.1 编程题 1
试题名称:排排坐时间限制:1.0 s内存限制:512.0 MB
思路:设座位从左到右的顺序为 a₁, a₂, …, aₙ,则第 i 个小朋友获得的糖果数是前 i 个数之和(前缀和)。把所有小朋友获得的糖果加起来,等价于把每个数字 aⱼ 按照"它出现在多少个前缀和里"来加权求和——位置越靠左的数字,出现在的前缀和个数越多(第 1 个位置的数字会出现在全部 n 个前缀和里,最后一个位置的数字只出现在最后 1 个前缀和里)。要让总和最大,就应该让数值越大的数字获得越大的权重,也就是把数字从大到小排列,最大的数放在最左边。这是一个典型的贪心/排序不等式应用:只要将数组按从大到小排序,再依次累加"当前前缀和",最终把所有前缀和相加即为答案。复杂度为 O(n log n)(排序)或本题数据范围下 O(n²) 的冒泡排序也完全可以接受(n ≤ 1000)。
#include<iostream>usingnamespacestd;int a[1010];voidbubble_sort(int n){for (int i = 0; i < n; ++i) {for (int j = 1; j < n - i; ++j) {if (a[j - 1] < a[j]) swap(a[j - 1], a[j]); // 从大到小排序 } }}intmain(){int n;cin >> n;for (int i = 0; i < n; ++i)cin >> a[i]; bubble_sort(n);int ans = 0, sum = 0;for (int i = 0; i < n; ++i) { sum += a[i]; // 当前前缀和,即第i个小朋友获得的糖果数 ans += sum; // 把每个小朋友获得的糖果累加进总答案 }cout << ans << endl;return0;}考点:
把"每个位置获得的糖果数是前缀和"这一规则转化为"每个数字被计入总和的次数(权重)与位置有关"的等价视角,是解决本题的关键一步; 排序不等式(权重从大到小时,数值也应从大到小配对才能让加权和最大)是贪心排序类问题的通用理论依据; 累加前缀和的同时累加到答案变量中,一次遍历即可得到最终结果,不需要额外存储所有前缀和。
3.2 编程题 2
试题名称:晚宴时间限制:1.0 s内存限制:512.0 MB
思路:题目要求从 n 个菜肴中选出两个互质(最大公约数为 1)的菜肴,使得它们的美味度之和最大。由于 n ≤ 1000,可以直接枚举所有 (i, j) 二元组(i<j),对每一对计算 gcd(arr[i], arr[j]),只有等于 1 时才是合法组合,在所有合法组合中取美味度之和的最大值。时间复杂度为 O(n²·log(max v))(gcd 本身是对数级别),在 n ≤ 1000 的数据范围下(约 10⁶ 次 gcd 调用)完全能在 1 秒内跑完。
#include<iostream>#include<algorithm>usingnamespacestd;intgcd(int a, int b){if (b == 0) return a;return gcd(b, a % b);}int arr[1010];intmain(){int n;cin >> n;for (int i = 0; i < n; ++i)cin >> arr[i];int ans = 0;for (int i = 0; i < n; ++i)for (int j = i + 1; j < n; ++j)if (gcd(arr[i], arr[j]) == 1) ans = max(ans, arr[i] + arr[j]);cout << ans << endl;return0;}考点:
判断两数是否互质,最直接的方法就是调用 gcd 函数并检验其返回值是否为 1; 数据范围不大时(n ≤ 1000),O(n²) 枚举所有二元组是完全可行且最直观的做法,不需要为了追求更优复杂度而引入额外的数论技巧(如按最大美味度从大到小排序、提前剪枝等,属于进阶优化思路,本题标准数据下并非必需); 用 i<j 的方式枚举避免重复统计同一对组合,也避免了 i==j 自己和自己配对的非法情况。
四、知识点分类汇总:这套五级卷子各考点考了几道?
一句话看懂这张表:
五级相比四级,考点重心进一步从"函数与指针基础"上升到了"链表结构操作 + 排序算法原理 + 数论基础"三大板块,这三块合计 16 道题,占了单选和判断总数的三分之二左右,标志着五级开始真正进入"数据结构与算法"的核心地带; 链表题不再停留在"是否会用指针"这个层面,而是深入考察插入/删除操作中"指针修改顺序"是否正确,以及循环链表遍历时"终止条件"的特殊处理,这类细节题是五级区别于四级最典型的"能力台阶"; 数论板块(gcd、质数筛法、唯一分解定理)首次成规模出现,说明五级要求考生掌握基础数论工具,为后续更高等级中数论、组合数学类题目打基础; 排序算法从"识别是哪种排序"进一步升级为"分析排序过程中某个操作的调用次数"、"辨析归并/快排的稳定性",考察的是对算法执行过程的精确推演能力,而不只是记住排序步骤; 二分答案(第10题)作为"二分查找"的变体首次出现,是本套卷子里较有区分度的一道题,说明五级已经开始涉及"用二分法解决判定性问题"这一进阶技巧。
也就是说,五级想稳过,重心要放在链表操作的指针顺序细节、排序算法的原理与稳定性辨析、基础数论工具(gcd/质数筛)这三块,同时要开始适应"二分答案"这种把二分查找用于求解最优化问题的思维方式。
如果想要更系统地刷 GESP 历年真题、看考点分布统计,或者做针对性专项训练,可以去 www.gesppass.com 看看,站内整理了各级别的真题详解和知识点归纳,从三级到五级的真题解析风格保持一致,方便按等级循序渐进地查漏补缺、检验学习效果。

夜雨聆风