题解|GESP2026年3月C++八级真题解析

四季读书网 3 0
题解|GESP2026年3月C++八级真题解析

1. 单选题(每题 2 分,共 30 分)

题解|GESP2026年3月C++八级真题解析 第1张

【答案】: D。

【解析】: 至少 1 名男生和 1 名女生有两种情况,1 男 2 女以及 2 男 1 女。其中 1 男 2 女的方案数为 ,表示 8 名男生中选出 1 名,6 名女生中选出 2 名,同理,2 男 1 女的方案数为 。总方案数为 

题解|GESP2026年3月C++八级真题解析 第2张

【答案】: B。

【解析】: 第 10 行的所有数之和就是 ,根据二项式定理或者从子集个数角度理解,这个求和公式就是 

题解|GESP2026年3月C++八级真题解析 第3张

【答案】: B。

【解析】: 每次 e 都除以 ,时间复杂度为 

题解|GESP2026年3月C++八级真题解析 第4张

【答案】: C。

【解析】: 问至少,考虑正难则反。总方案有 ,不包含数学书的方案为 ,那么至少包含  本数学书的方案就有 

题解|GESP2026年3月C++八级真题解析 第5张

【答案】: B。

【解析】: 这棵树的左子树包含 ,右子树包含 ,形态可能有多种。

  • A 选项错误,这棵树不一定是完全二叉树,例如, 的左子树中  是  的左儿子。
  • B 选项正确,如果  是兄弟节点,那么他们的父亲一定是介于  之间的数,这样的数不存在。
  • C 选项错误, 的最大深度为 ,例如, 的左子树中  是  的左儿子。
  • D 选项错误, 可以是  的右儿子, 是  的左儿子。
题解|GESP2026年3月C++八级真题解析 第6张

【答案】: C。

【解析】: 基础题。

题解|GESP2026年3月C++八级真题解析 第7张

【答案】: B。

【解析】: 最短路最多经过  条边,路径中不会有环,因为有环那么环上的路径和一定 ,不会使得最短路变小。

题解|GESP2026年3月C++八级真题解析 第8张

【答案】: B。

【解析】: 用 Dijkstra 算法求出任意两点间的最短路后,再用 Floyd 算法求解,任意两点间的最短路不会发生改变,已经满足最短路性质(三角不等式)。

题解|GESP2026年3月C++八级真题解析 第9张

【答案】: B。

【解析】: 考察最短路算法的理解。

  • A 选项错误,Dijkstra 算法不能处理负权边的情况。
  • B 选项正确。
  • C 选项错误,单源最短路算法可以求解无向图。
  • D 选项错误,Dijkstra 算法每一步会选择一个未访问的距离最小的点。
题解|GESP2026年3月C++八级真题解析 第10张

【答案】: C。

【解析】: 甲、乙两人要相邻,那么就用捆绑法。将甲、乙看成一个整体,内部可以交换顺序,有  种情况,然后看成  个人的排队,共有  种方案。我们需要排除掉甲、乙两人相邻且丙在排头的方案,丙在排头,那么先排丙,有  种方案,剩下可以看成  个人的全排列,且甲、乙内部可以交换顺序(因为甲、乙看成一个整体),那么甲、乙两人相邻且丙在排头的方案共有  种方案。总共合法方案为  种方案。

题解|GESP2026年3月C++八级真题解析 第11张

【答案】: C。

【解析】: 如果  并且  可达,那么就可以用  来松弛 ,也就是 C 选项的代码。这里解释一下 A 选项为什么错误:首先因为是单选题,C 选项的逻辑更完整;其次,A 选项可能会导致 dist[i][k] + dis[k][j] 的值溢出,最终导致转移的含义出现偏差。

题解|GESP2026年3月C++八级真题解析 第12张

【答案】: B。

【解析】: 个位只有  三种选择,分别讨论这  种情况。当个位为  时,剩下  位随便放,有  种方案;当个位为  或  时,首位不能为 ,那么从高位到低位依次有  种可能性,方案数为 。总方案数为 

题解|GESP2026年3月C++八级真题解析 第13张

【答案】: A。

【解析】: 如果别的点 v 可以通过到达 u 到达最小生成树,并且比之前到达最小生成树的距离更小,那么就更新 v 到达生成树的最小距离,也就是 A 选项。这里说一下为什么不选 B 选项:首先因为是单选题,A 选项逻辑更完整;其次,B 选项没有判断 u,v 之间是否有边相连,万一这个代码中没有边相连用的是负数、 等来表示,那么更新到生成树的距离的时候就会有问题。

题解|GESP2026年3月C++八级真题解析 第14张

【答案】: C。

【解析】: 三点共线可以用斜率相等来判断,也就是  来判断,这个式子转化为代码就是 C 选项。

题解|GESP2026年3月C++八级真题解析 第15张

【答案】: A。

【解析】:sizeof(a) 求解的是 a 数组的字节大小,一个 int 占用  个字节,共  个字节;p 是一个指向数组 int [4] 的指针,在 64 位系统下,指针变量占  个字节; 仍是一个指针,占  个字节;q 是指向数组 a 首地址的指针,也占用  个字节。因此选 A。

2. 判断题(每题 2 分,共 20 分)

2.1 判断题题面

题解|GESP2026年3月C++八级真题解析 第16张

2.2 判断题解析

第 1 题static 成员变量不属于结构体对象,属于结构体本身,不属于任何实例,存储在静态区(也称全局数据区)。

第 2 题。二项式定理基础内容。

第 3 题const int & 表示常引用,既可以绑定左值,又可以绑定右值。

第 4 题。最小生成树唯一,不一定图中所有边权各不相同,只要权值相同的边不影响最小生成树的唯一性即可。

第 5 题。最坏情况时间复杂度会退化为 

第 6 题。所有顶点度数为偶数时存在欧拉回路的必要条件,但还需要非零度顶点在同一个连通分量中。

第 7 题。ST 表预处理时间复杂度为 ,每次查询时间复杂度为 

第 8 题。边的相对大小不会发生变化,因此正确。

第 9 题。考察最小生成树算法基础。

第 10 题。当两种方式的“状态空间”数不同时,时间复杂度就不一样。

3. 编程题(每题 25 分,共 50 分)

3.1 编程题 1(消息查找)

题解|GESP2026年3月C++八级真题解析 第17张
题解|GESP2026年3月C++八级真题解析 第18张

分析

这道题本质上是个「最短路径」问题。

总共有  个点,有两种类型的边,普通边:对于任意  都有一条  权重为  的边(表示向前翻了一条消息);引用边:如果消息  引用了 ,那么就存在一条  权重为  的边(表示跳转到引用的消息)。

这些边都是从编号大的指向编号小的,整个图是一个有向无环图(DAG)。每次询问从  点走到  点的最短路。

如果我们把整个图建出来跑最短路,那么对于每次查询需要  的时间复杂度,总时间复杂度为 ,只能拿到暴力分。

题目保证至多只有  条引用消息。那么最多只有  个「关键点」拥有跳跃能力(引用边的起点)或者跳跃的落点(引用边的终点)。因此,我们可以只关注「关键点」,将图压缩。

将所有涉及引用的消息编号提取出来,作为「关键点」,这样的关键点最多  个。

因为是 DAG 图,就可以用 DP 预处理出任意两个「关键点」之间的最短路,每次查询的时候我们只需要计算出距离起点  最近的关键点 ,距离终点最近的关键点 ,然后就可以  的回答  的最短路了,就是 

具体地:

将关键点按编号从小到大排序,记为 。因为是 DAG 图,只会向编号小的点移动,那么就可以用 DP 预处理出任意两个关键点之间的最短路。

设  表示  这个关键点到  这个关键点的最短路。

我们倒序递推到更小关键点,天然满足拓扑序:

对于每个起点 i:    对于当前点 j (从 i 递减到 1):        1. 可以走到前一个关键点 j-1(行走边)        2. 如果 p[j] 有引用,可以跳到 r[p[j]](跳跃边)

状态转移:

dp[i][j - 1] = min(dp[i][j - 1], dp[i][j] + (a[j] - a[j - 1])); // 转移 1:普通边if (r[a[j]] > 0) { // 转移 2:跳跃边(如果 a[j] 有引用)int k = pos[r[a[j]]];  // 引用目标在关键点中的下标    dp[i][k] = min(dp[i][k], dp[i][j] + 1);}

至于求解离起点  最近的关键点 ,和离终点  最近的关键点  的做法有很多:

  • 方法1:直接二分查找求解,每次查询需要  复杂度。

  • 方法2:预处理 pre[i] 和 suf[i] 数组,分别表示离  最近的  的关键点和离  最近的  的关键点,然后  递推这两个数组的,每次查询是  的。

至此,对于每一次询问,找到距离  最近的  的关键点 ,以及距离  最近的  的关键点 ,答案就由三部分组成,。特殊地:当  时,说明  和  中间没有特殊点,答案就是 

总时间复杂度为 ,其中  表示关键点的数量,不超过 

可能有人有疑问为什么选择最近的关键点最优?

这个很容易证明,我们以距离  最近的关键点  举例,假设入口选择更小的关键点 ,那么代价  为 ,代价  为 ,我们只需要比较  和  的大小。从  走到  一定会先经过  这个关键点,且中间没有其他关键点。那么  可以拆分成 ,而最短路网络中一定有 ,所以 

同理,可以证明对于出口也一定是离  最近的  出去的。

换句话说,从  到任何更小的关键点一定都要经过 ,从任何更大的关键点到  都要先经过 ,所以选择它们作为关键网络的入口和出口一定是最优的。

代码

#include<bits/stdc++.h>usingnamespacestd;constint inf = 0x3f3f3f3f;constint maxn = 1e5 + 5;int r[maxn], pos[maxn], pre[maxn], suf[maxn], n, q;int a[2005], dp[2005][2005], tot;bool vis[maxn];intmain(){cin>>n>>q;for(int i = 1; i <= n; ++i) {cin>>r[i];if(r[i]) vis[i] = vis[r[i]] = true;    }for(int i = 1; i <= n; ++i) {if(vis[i]) { // 关键点            a[++tot] = i;            pos[i] = tot;        }    }memset(dp, 0x3fsizeof(dp));for(int i = 1; i <= tot; ++i) {        dp[i][i] = 0;for(int j = i; j >= 2; --j) { // 刷表法:已知状态去更新后续状态            dp[i][j - 1] = min(dp[i][j - 1], dp[i][j] + a[j] - a[j - 1]); // 普通边if(r[a[j]]) { // 引用边int k = pos[r[a[j]]];                dp[i][k] = min(dp[i][k], dp[i][j] + 1);            }        }    }for(int i = 1; i <= n; ++i) {        pre[i] = pre[i - 1];if(vis[i]) pre[i] = i;    }    suf[n + 1] = n + 1;for(int i = n; i >= 1; --i) {        suf[i] = suf[i + 1];if(vis[i]) suf[i] = i;    }while(q--) {int x, y;cin>>x>>y;int tmpx = pre[x], tmpy = suf[y], ans;if(tmpx < tmpy) ans = x - y;else ans = x - tmpx + dp[pos[tmpx]][pos[tmpy]] + tmpy - y;cout<<ans<<'\n';    }return0;}

3.2 编程题 2(子图最短路)

题解|GESP2026年3月C++八级真题解析 第19张
题解|GESP2026年3月C++八级真题解析 第20张

分析

总共有  个区间,对于每一个区间用 Floyed 算法跑最短路时间复杂度为 ,总时间复杂度为 。对于满足数据会超时,需要考虑优化。

注意到区间是连续的 。如果我们固定左端点 ,让右端点  从  慢慢增加到 

  • :只有 1 个点。
  • :增加 1 个点,增加一些边。
  • :再增加 1 个点...

当  增加  时,我们只需要更新涉及新点  的最短路径,而不需要重新计算所有路径!换句话说,当  增加  的时候,将  加入子图,作为 Floyed 算法中的中转点,然后更新所有点对新增一个中转点  的最短路。更新完之后,累加当前子图中所有点对的最短路。

总时间复杂度为 ,考察对 Floyed 算法的理解。

代码

#include<bits/stdc++.h>usingnamespacestd;typedeflonglong ll;constint maxn = 105, mod = 1e9, inf = 0x3f3f3f3f;int g[maxn][maxn], n, m;ll dp[maxn][maxn], ans;intmain(){cin>>n>>m;memset(g, 0x3fsizeof(g));for(int i = 1; i <= n; ++i) g[i][i] = 0;while(m--) {int u, v, w;cin>>u>>v>>w;        g[u][v] = g[v][u] = min(g[u][v], w);    }for(int l = 1; l <= n; ++l) {for(int i = 1; i <= n; ++i) {for(int j = 1; j <= n; ++j) dp[i][j] = g[i][j];        }for(int r = l; r <= n; ++r) {for(int i = 1; i <= n; ++i) {for(int j = 1; j <= n; ++j) dp[i][j] = min(dp[i][j], dp[i][r] + dp[r][j]);            }for(int i = l; i <= r; ++i) {for(int j = i; j <= r; ++j) {if(dp[i][j] == inf) continue;                    ans = (ans + dp[i][j]) % mod;                }            }        }    }cout<<ans;return0;}

如果你喜欢这类赛事真题解析文章,欢迎点赞、转发、收藏,让更多人看到这份真诚的分享!

欢迎点击下方名片关注

抱歉,评论功能暂时关闭!