GESP 客观题评测系统

试卷解析 · 理解每一步

洛谷 SCP-S 2026 第一轮模拟赛(提高级 C++)

CSPS-2026-R1-Mock-Luogu

试卷解析总览,可直接查看每题答案与解析。

一、单项选择题

第 1 题(单选题,2 分)

在 Linux 系统下本地测试函数式交互题,源程序名为 `scp.cpp`,源程序内引用了头文件 `scp.h`,题目下发交互库为 `grader.cpp`,则以下哪条编译命令可以输出正确的可执行文件 `scp`?( )

A.
`g++ grader.cpp scp.h -o scp -O2 -std=c++14 -static`
B.
`g++ grader.cpp scp.cpp -o scp -O2 -std=c++14 -static`
C.
`g++ scp.h scp.cpp -o scp -O2 -std=c++14 -static`
D.
`g++ scp.cpp scp.h -o scp -O2 -std=c++14 -static`

正确答案B

解析详情
答案 BC++ 多文件编译与链接
思路与推导
  1. `grader.cpp` 和 `scp.cpp` 都是需要编译并链接的源文件;`scp.h` 已由源程序包含,不应代替 `scp.cpp` 参与编译。参数 `-o scp` 指定输出可执行文件名,因此应选 B。

第 2 题(单选题,2 分)

关于最短路算法,以下说法中完全正确的是。( )

A.
只要图中没有负环,Dijkstra 算法就一定能求出正确的最短路径。
B.
SPFA 算法在任何图上的最坏时间复杂度都比 Dijkstra 优秀。
C.
标准的 Dijkstra 算法(基于贪心,结点出队后不再更新)在包含负权边的图中可能会得到错误的结果。
D.
Floyd 算法只能求任意两点间的最短路,无法判断图中是否存在负环。

正确答案C

解析详情
答案 C最短路算法适用条件
思路与推导
  1. 标准 Dijkstra 在顶点出队后固定其最短距离,这一步依赖边权非负;存在负权边时,已固定的距离可能后来被进一步缩短。SPFA 最坏为 O(VE)O(VE),而 Floyd 还可通过 d[i][i]<0d[i][i]<0 判断负环。

第 3 题(单选题,2 分)

某算法的时间复杂度的递归式为 T(n)=2T(n)+Θ(log⁡n)T(n)=2T(\sqrt n)+\Theta(\log n),则其渐进时间复杂度为?( )

A.
Θ(log⁡n)\Theta(\log n)
B.
Θ(log⁡log⁡n)\Theta(\log\log n)
C.
Θ(log⁡2n)\Theta(\log^2 n)
D.
Θ(log⁡nlog⁡log⁡n)\Theta(\log n\log\log n)

正确答案D

解析详情
答案 D递归式与主定理
思路与推导
  1. 令 m=log⁡nm=\log n、S(m)=T(2m)S(m)=T(2^m),则 n=2m/2\sqrt{n}=2^{m/2},原式化为 S(m)=2S(m/2)+Θ(m)S(m)=2S(m/2)+\Theta(m)。由主定理得 S(m)=Θ(mlog⁡m)S(m)=\Theta(m\log m),故 T(n)=Θ(log⁡nlog⁡log⁡n)T(n)=\Theta(\log n\log\log n)。

第 4 题(单选题,2 分)

使用数 1, 2, 3, 4, 5 各一个以及 2 个加号、2 个减号可组成的后缀表达式的值最大为?( )

A.
9
B.
11
C.
12
D.
13

正确答案D

解析详情
答案 D后缀表达式与运算符树
思路与推导
  1. 两个减号无论怎样嵌套,最终至少有一个数的系数为负,因此最大值不超过 1+2+3+4+5−2×1=131+2+3+4+5-2\times1=13。表达式 5+4+3−(1−2)=135+4+3-(1-2)=13 恰用两个加号和两个减号,其后缀式为 `5 4 + 3 + 1 2 - -`。

第 5 题(单选题,2 分)

运行如下 C++ 代码片段后,关于数组 `v` 的状态,下述说法正确的是?( )

std::vector<int> v = {9, 2, 7, 4, 5, 8, 1, 3, 6};
std::nth_element(v.begin(), v.begin() + 3, v.end());
A.
`v[3]` 的值必定是 4,且 `v[0]` 到 `v[2]` 的值必定依次是 1, 2, 3。
B.
`v[3]` 的值必定是 4,且 `v[0]` 到 `v[2]` 包含 1, 2, 3 这三个数。
C.
`v[3]` 的值必定是 4,且整个数组已经被完全排序。
D.
`v[0]` 到 `v[3]` 包含 1, 2, 3, 4,且右侧的 `v[4]` 到 `v[8]` 必定已按从小到大排序。

正确答案B

解析详情
答案 B`std::nth_element`
思路与推导
  1. `v.begin()+3` 指向按升序排列后的第 4 个元素,所以调用后 `v[3]=4`。其左侧三个位置都不大于 4,因元素互异,恰好包含 1,2,31,2,3,但三者内部次序没有保证。

第 6 题(单选题,2 分)

使用 `std::set` 维护有序集合时,设集合 ss中现有nn个元素,则使用 `std::lower_bound(s.begin(), s.end(), x)` 查找第一个≥x\ge x 的元素的时间复杂度为?( )

A.
Θ(log⁡n)\Theta(\log n)
B.
Θ(log⁡2n)\Theta(\log^2 n)
C.
Θ(n)\Theta(n)
D.
Θ(nlog⁡n)\Theta(n\log n)

正确答案C

解析详情
答案 C迭代器类别与二分查找复杂度
思路与推导
  1. 通用 `std::lower_bound` 在 `set` 的双向迭代器上虽只需 O(log⁡n)O(\log n) 次比较,但移动迭代器到中点累计需要 Θ(n)\Theta(n) 次递增。因此总时间为 Θ(n)\Theta(n);成员函数 `s.lower_bound(x)` 才是 Θ(log⁡n)\Theta(\log n)。

第 7 题(单选题,2 分)

以下选项中,哪个序列不可能是对长度为 6 的字符串 SS运行 KMP 算法得到的 `next` 数组(定义 `next[i]` 为S[1…i]S[1\ldots i] 的最长相等真前后缀长度)?( )

A.
`0 0 0 1 2 3`
B.
`0 0 0 1 2 1`
C.
`0 1 0 1 2 1`
D.
`0 1 2 0 1 2`

正确答案C

解析详情
答案 CKMP 前缀函数
思路与推导
  1. 序列 C 中 `next[2]=1,next[3]=0,next[4]=1,next[5]=2` 迫使前五个字符形如 `aabaa`。若 `next[6]=1`,第 6 个字符必须为 `a`,此时末尾 `aa` 又与开头 `aa` 相同,故 `next[6]` 至少为 2,产生矛盾。

第 8 题(单选题,2 分)

对 10 个互不相同且均在 [0,31][0,31] 之间的整数建立深度为 5(定义为叶子到根的距离)的 01-Trie,所可能得到的最多结点数和最少结点数之差为?( )

A.
0
B.
13
C.
22
D.
55

正确答案B

解析详情
答案 B01-Trie 结点计数
思路与推导
  1. 结点最多时,各层不同前缀数为 2,4,8,10,102,4,8,10,10,连同根共 3535 个。结点最少时尽量共享前缀,各层至少为 1,2,3,5,101,2,3,5,10,连同根共 2222 个,差为 35−22=1335-22=13。

第 9 题(单选题,2 分)

使用数据结构维护序列区间信息 al⊕al+1⊕⋯⊕ara_l\oplus a_{l+1}\oplus\cdots\oplus a_r,其中 ⊕\oplus 为一般二元运算,下列说法正确的是?( )

A.
ST 表能 Θ(1)\Theta(1)回答区间询问的原因是利用了区间的重叠,要求⊕\oplus 有交换律。
B.
若使用线段树进行单点修改和区间查询,⊕\oplus 可以不满足结合律,但必须满足交换律。
C.
基于前缀差分方式实现的树状数组,要询问区间信息,则要求 ⊕\oplus 必须可逆(存在逆元)。
D.
若使用平衡树维护序列区间信息,⊕\oplus 必须同时满足交换律和结合律,否则无法合并。

正确答案C

解析详情
答案 C区间运算的代数性质
思路与推导
  1. 树状数组用前缀信息求区间 [l,r][l,r] 时,需要从前缀 rr 中消去前缀 l−1l-1,因此运算必须存在逆元。线段树合并要求结合律而非交换律;普通 ST 表利用重叠还要求幂等性。

第 10 题(单选题,2 分)

对于一个包含 10 个顶点的无向简单图 GG,设其补图为 G‾\overline G,下列说法正确的是?( )

A.
若 GG不连通,那么G‾\overline G 必然也不连通。
B.
存在某个 GG,使得 GG和G‾\overline G 都是一棵树。
C.
存在某个 GG,使得 GG和G‾\overline G 同时为二分图。
D.
不存在 GG,使得 GG和G‾\overline G 同时拥有欧拉回路。

正确答案D

解析详情
答案 D补图与欧拉回路
思路与推导
  1. 对任一顶点 vv,有 deg⁡G(v)+deg⁡G‾(v)=9\deg_G(v)+\deg_{\overline{G}}(v)=9。若 GG 与补图都拥有欧拉回路,则该顶点在两图中的度都应为偶数,其和也应为偶数,与 9 为奇数矛盾。

第 11 题(单选题,2 分)

计算矩阵 [1110]2026\begin{bmatrix}1&1\\1&0\end{bmatrix}^{2026} 每项对 3 取模的值为?( )

A.
[1220]\begin{bmatrix}1&2\\2&0\end{bmatrix}
B.
[1221]\begin{bmatrix}1&2\\2&1\end{bmatrix}
C.
[2110]\begin{bmatrix}2&1\\1&0\end{bmatrix}
D.
[2111]\begin{bmatrix}2&1\\1&1\end{bmatrix}

正确答案D

解析详情
答案 D矩阵快速幂与 Fibonacci 数列
思路与推导
  1. 设 M=[1110]M=\begin{bmatrix}1&1\\1&0\end{bmatrix},则 Mk=[Fk+1FkFkFk−1]M^k=\begin{bmatrix}F_{k+1}&F_k\\F_k&F_{k-1}\end{bmatrix}。Fibonacci 数列模 3 的周期为 8,而 2026≡2(mod8)2026\equiv2\pmod{8},故结果为 [2111]\begin{bmatrix}2&1\\1&1\end{bmatrix}。

第 12 题(单选题,2 分)

有一个正十二面体,共 20 个顶点、30 条棱,每个面都是正五边形,其棱长为 1,顶点之间只能通过棱互相到达,则所有 (202)\binom{20}{2} 个点对间最短路长度之和为?( )

A.
420
B.
500
C.
600
D.
760

正确答案B

解析详情
答案 B正十二面体图的距离分布
思路与推导
  1. 从任一顶点出发,到距离 1,2,3,4,51,2,3,4,5 的顶点数依次为 3,6,6,3,13,6,6,3,1,距离和为 3+12+18+12+5=503+12+18+12+5=50。对 20 个起点求和后每个无序点对被计算两次,故总和为 20×50/2=50020\times50/2=500。

第 13 题(单选题,2 分)

有一排 10 盏灯,初始时全部为关闭状态;每次操作可以选定一个连续区间,并将该区间内的灯亮灭状态反转,则恰好 3 次操作后,这 10 盏灯可能呈现出多少种不同的亮灭状态?( )

A.
120
B.
512
C.
848
D.
1024

正确答案C

解析详情
答案 C区间异或与差分
思路与推导
  1. 用 11 个边界位表示灯串差分,一次区间反转恰翻转两个边界位。三次操作后边界向量的非零位数只能为 0,2,4,60,2,4,6,这些情况都可通过拆分区间或重复操作实现,故状态数为 (110)+(112)+(114)+(116)=848\binom{11}{0}+\binom{11}{2}+\binom{11}{4}+\binom{11}{6}=848。

第 14 题(单选题,2 分)

对于长度为 2026 的排列 pp,初始时对于所有位置 ii均有pi=ip_i=i;现在进行恰好 10410^4次操作,每次操作可以交换相邻两个位置上的数,则操作完成后得到的新排列p′p' 的最长上升子序列长度最短是多少?( )

A.
110
B.
183
C.
186
D.
187

正确答案D

解析详情
答案 D逆序数与最长上升子序列
思路与推导
  1. 若最长上升子序列至多为 LL,最少逆序数由 LL 个尽量等大的下降块取得。L=186L=186 时最少为 20(102)+166(112)=10030>10420\binom{10}{2}+166\binom{11}{2}=10030>10^4;L=187L=187 时可构造 187 个下降块并微调为 9976 个逆序,再用 24 次往返交换补足操作数。故最短为 187。

第 15 题(单选题,2 分)

对于如下图所示的无向图 GG,在所有将边赋予 [1,7][1,7] 中互不相同的整数边权的方案中,GG 的最小生成树的权值和之和为?( )

无向图 G
A.
50400
B.
53328
C.
63408
D.
80640

正确答案B

解析详情
答案 BKruskal 算法与排列计数
思路与推导
  1. 图中 7 条边的权值是 11 到 77 的一个排列,共 7!=50407!=5040 种。按 Kruskal 枚举,生成树权值为 10,11,12,1310,11,12,13 的方案数分别是 3024,1200,720,963024,1200,720,96,故总和为 3024×10+1200×11+720×12+96×13=533283024\times10+1200\times11+720\times12+96\times13=53328。

二、阅读程序(1)

(1)

 1 | #include <bits/stdc++.h>
 2 | using namespace std;
 3 |
 4 | const int N = 20, mod = 998244353;
 5 | int n, a[N][N], f[1 << N];
 6 |
 7 | int main() {
 8 |     cin >> n;
 9 |     for (int i = 0; i < n; i++)
10 |         for (int j = 0; j < n; j++)
11 |             cin >> a[i][j];
12 |     f[0] = 1;
13 |     for (int i = 0; i < (1 << n); i++)
14 |         for (int j = 0; j < n; j++)
15 |             if (i >> j & 1)
16 |                 (f[i] += 1ll * f[i ^ (1 << j)] *
17 |                          a[__builtin_popcount(i) - 1][j] % mod) %= mod;
18 |     cout << f[(1 << n) - 1];
19 |     return 0;
20 | }

保证输入的整数满足 1≤n≤201\le n\le20,1≤ai,j<9982443531\le a_{i,j}<998244353,完成下面的判断题和单选题。

第 16 题(判断题,1 分)

当 n=20n=20 时,程序不会出现数组越界访问。( )

正确答案正确

解析详情
答案 正确数组边界与状态压缩
思路与推导
  1. 当 n=20n=20 时,`a` 只访问下标 `0` 到 `19`,`f` 的最大下标为 `(1 << 20) - 1`,均在声明范围内。第 17 行仅在掩码含第 `j` 位时执行,此时 `popcount(i)-1` 也不会为负。

第 17 题(判断题,1.5 分)

只要输入符合限定要求,无论输入的是什么,输出都一定是正整数。( )

正确答案错误

解析详情
答案 错误模运算与计数 DP
思路与推导
  1. 输出是所有排列乘积之和对 P=998244353P=998244353 取模,正数取模后可能为 0。例如 n=2n=2、矩阵为 [11P−11]\begin{bmatrix}1&1\\P-1&1\end{bmatrix} 时,结果为 1+(P−1)≡0(modP)1+(P-1)\equiv0\pmod{P}。

第 18 题(判断题,1.5 分)

删除第 16 行的 `1ll *`,不会对程序的输出产生任何影响。( )

正确答案错误

解析详情
答案 错误整数提升与乘法溢出
思路与推导
  1. `f[...]` 与 `a[...]` 都可能接近 998244353998244353,乘积约为 101810^{18},超过 32 位 `int` 范围。`1ll *` 使乘法在 `long long` 中完成;删除后会先发生有符号整数溢出,输出可能改变。

第 19 题(单选题,3 分)

当输入为 `3 1 2 3 2 3 1 3 1 2` 时,输出为( )?

A.
52
B.
53
C.
54
D.
55

正确答案C

解析详情
答案 C子集 DP 与矩阵 permanent
思路与推导
  1. 程序计算矩阵 permanent,即对 6 个列排列累加对应乘积。该输入的 6 项为 6,1,8,6,6,276,1,8,6,6,27,总和为 6+1+8+6+6+27=546+1+8+6+6+27=54,因此输出 54。

第 20 题(单选题,3 分)

该代码的时间复杂度为( )?

A.
Θ(n2)\Theta(n^2)
B.
Θ(2n)\Theta(2^n)
C.
Θ(n×2n)\Theta(n\times2^n)
D.
Θ(n2×2n)\Theta(n^2\times2^n)

正确答案C

解析详情
答案 C状态压缩 DP 复杂度
思路与推导
  1. 外层枚举全部 2n2^n 个掩码,内层对每个掩码固定枚举 nn 个列位置;位判断和转移均为常数时间。因此总时间复杂度为 Θ(n×2n)\Theta(n\times2^n)。

第 21 题(单选题,3 分)

当 n=14n=14 时,代码第 15 行的 `if` 语句中,表达式值为真的次数是( )?

A.
114688
B.
229176
C.
114588
D.
229376

正确答案A

解析详情
答案 A位掩码计数
思路与推导
  1. 对固定的第 `j` 位,在 2142^{14} 个掩码中恰有一半即 2132^{13} 个掩码含该位。共有 14 个 `j`,所以 `if` 条件为真的总次数为 14×213=11468814\times2^{13}=114688。

二、阅读程序(2)

(2)

 1 | #include <bits/stdc++.h>
 2 | using namespace std;
 3 |
 4 | typedef long long ll;
 5 | ll len[90], dp[90][2];
 6 |
 7 | void init() {
 8 |     len[0] = 1, len[1] = 2, dp[1][1] = 1;
 9 |     for (int i = 2; i <= 86; i++) {
10 |         len[i] = len[i - 1] + len[i - 2];
11 |         int op = len[i - 1] & 1;
12 |         dp[i][0] = dp[i - 1][0] + dp[i - 2][0 ^ op];
13 |         dp[i][1] = dp[i - 1][1] + dp[i - 2][1 ^ op];
14 |     }
15 | }
16 |
17 | int solve1(int n, int p) {
18 |     string s = "0", nxt = "01", tmp;
19 |     while (nxt.length() < n)
20 |         tmp = nxt + s, s = nxt, nxt = tmp;
21 |     int ans = 0;
22 |     for (int i = p; i < n; i += 2) ans += nxt[i] - '0';
23 |     return ans;
24 | }
25 |
26 | int value(int pos) {
27 |     if (pos <= 1) return pos;
28 |     int k = 0;
29 |     while (len[k + 1] <= pos) k++;
30 |     return value(pos - len[k]);
31 | }
32 |
33 | int solve2(int n, int p) {
34 |     int ans = 0;
35 |     for (int i = p; i < n; i += 2) ans += value(i);
36 |     return ans;
37 | }
38 |
39 | ll solve3(ll n, int p) {
40 |     int k = 0; ll ans = 0;
41 |     while (len[k + 1] <= n) k++;
42 |     for (; k >= 0; k--) if (len[k] <= n)
43 |         ans += dp[k][p], n -= len[k], p ^= len[k] & 1;
44 |     return ans;
45 | }
46 |
47 | int main() {
48 |     ll n; int p;
49 |     cin >> n >> p, init();
50 |     if (n <= 1e6)
51 |         cout << solve1(n, p) << ' ' << solve2(n, p) << ' '
52 |              << solve3(n, p) << endl;
53 |     else
54 |         cout << solve3(n, p) << endl;
55 |     return 0;
56 | }

假设输入的整数满足 1≤n≤10181\le n\le10^{18},p∈{0,1}p\in\{0,1\},完成下面的判断题和单选题。

第 22 题(判断题,1.5 分)

`solve1` 函数中使用朴素的字符串拼接,时间复杂度为 Θ(n2)\Theta(n^2)。( )

正确答案错误

解析详情
答案 错误字符串递推的摊还复杂度
思路与推导
  1. 拼接出的字符串长度按 Fibonacci 数增长,历次拼接复制的字符总数是 F1+F2+⋯+Fk=O(Fk+2)=O(n)F_1+F_2+\cdots+F_k=O(F_{k+2})=O(n)。随后扫描约 n/2n/2 个位置,因此 `solve1` 总时间为 Θ(n)\Theta(n),不是 Θ(n2)\Theta(n^2)。

第 23 题(判断题,1.5 分)

将第 11 行的 `len[i - 1] & 1` 改为 `i % 3 < 2`,程序仍然能正常运行,且输出结果不变。( )

正确答案正确

解析详情
答案 正确Fibonacci 数列的奇偶周期
思路与推导
  1. `len` 满足 Fibonacci 型递推,其奇偶性以“奇、偶、奇”为周期。对 i=2,3,4,…i=2,3,4,\ldots,`len[i-1]&1` 的序列与 `i%3<2` 都是“0,1,1”循环,因此 `op` 始终相同,`dp` 和输出不变。

第 24 题(判断题,1.5 分)

对于输入范围内的所有 nn,`solve3(n, 0)` 与 `solve3(n, 1)` 的返回值之差的绝对值均不超过 1。( )

正确答案错误

解析详情
答案 错误Fibonacci 字与奇偶位置计数
思路与推导
  1. 取 n=15n=15,前 15 个字符为 `010010100100101`。偶数下标上的 1 有 4 个,奇数下标上的 1 有 2 个,所以 `solve3(15,0)` 与 `solve3(15,1)` 相差 2,已经超过 1。

第 25 题(单选题,3 分)

`solve2` 函数在最坏情况下的时间复杂度是?( )

A.
Θ(nlog⁡log⁡n)\Theta(n\log\log n)
B.
Θ(nlog⁡n)\Theta(n\log n)
C.
Θ(nlog⁡nlog⁡log⁡n)\Theta(n\log n\log\log n)
D.
Θ(nlog⁡2n)\Theta(n\log^2 n)

正确答案D

解析详情
答案 D递归查询复杂度
思路与推导
  1. `solve2` 要处理 Θ(n)\Theta(n) 个位置。`value(pos)` 最坏递归 Θ(log⁡n)\Theta(\log n) 层,而每层的 `while` 又从 `len[1]` 起扫描 Θ(log⁡n)\Theta(\log n) 项,故单次最坏为 Θ(log⁡2n)\Theta(\log^2 n),总计 Θ(nlog⁡2n)\Theta(n\log^2 n)。

第 26 题(单选题,3 分)

对于输入数据 `233 0`,输出结果的第一个数为?( )

A.
44
B.
45
C.
55
D.
72

正确答案B

解析详情
答案 BFibonacci 字的直接生成与计数
思路与推导
  1. 因 233≤106233\le10^6,程序输出的第一个数是 `solve1(233,0)`。生成长度不少于 233 的 Fibonacci 字后,统计下标 0,2,…,2320,2,\ldots,232 上的字符 1,共得到 45 个,因此第一个数为 45。

第 27 题(单选题,3 分)

若某次调用 `solve3(n, p)` 时,语句 `ans += dp[k][p]` 恰好执行了 8 次,则输入的 nn 最小可能是多少?( )

A.
986
B.
1596
C.
2583
D.
4180

正确答案B

解析详情
答案 BFibonacci 贪心分解
思路与推导
  1. 第 42—43 行执行次数等于 nn 的 Zeckendorf 分解项数。要有 8 项且使 nn 最小,应取最小的 8 个互不相邻 Fibonacci 长度:1,3,8,21,55,144,377,9871,3,8,21,55,144,377,987,其和为 15961596。

二、阅读程序(3)

(3)

 1 | #include <bits/stdc++.h>
 2 | using namespace std;
 3 |
 4 | typedef long long ll;
 5 | typedef unsigned long long ull;
 6 | const int N = 100005, M = 200005, S = 400037;
 7 | const int P = 998244353, iv2 = (P + 1) / 2;
 8 |
 9 | int n, m, w[M], sum, dep[N], ct[M], sw[M], cur[M], ans[N];
10 | ull h[N], he[M];
11 | bool vis[N];
12 | vector<pair<int, int> > G[N], T[N];
13 | mt19937_64 rnd(random_device{}());
14 |
15 | int tot, hd[S];
16 | struct node { int nxt; ull key; } mp[M];
17 | int get(ull key) {
18 |     int u = key % S;
19 |     for (int i = hd[u]; i; i = mp[i].nxt)
20 |         if (mp[i].key == key) return i;
21 |     return mp[++tot] = {hd[u], key}, hd[u] = tot;
22 | }
23 |
24 | void dfs1(int u, int p) {
25 |     vis[u] = 1;
26 |     for (auto [v, i] : G[u]) if (i ^ p) {
27 |         if (!vis[v])
28 |             dep[v] = dep[u] + 1, T[u].push_back({v, i}),
29 |             dfs1(v, i), h[u] ^= h[v], he[i] = h[v];
30 |         else if (dep[v] < dep[u])
31 |             he[i] = rnd(), h[u] ^= he[i], h[v] ^= he[i];
32 |     }
33 | }
34 |
35 | void dfs2(int u, ll c, ll b, ll b2) {
36 |     ans[u] = ((c + b * sum - (b * b + b2) % P * iv2) % P + P) % P;
37 |     for (auto [v, i] : T[u]) {
38 |         ll w = ::w[i];
39 |         if (!he[i]) {
40 |             dfs2(v, c, (b + w) % P, (b2 + w * w) % P);
41 |             continue;
42 |         }
43 |         int j = get(he[i]);
44 |         if (ct[j] == 1) dfs2(v, c, b, b2);
45 |         else {
46 |             int d = (w * (sw[j] - cur[j] * 2 - w) % P + P) % P;
47 |             cur[j] = (cur[j] + w) % P;
48 |             dfs2(v, (c + d) % P, b, b2);
49 |             cur[j] = (cur[j] - w + P) % P;
50 |         }
51 |     }
52 | }
53 |
54 | int main() {
55 |     ios::sync_with_stdio(0), cin.tie(0);
56 |     cin >> n >> m;
57 |     for (int i = 1, u, v; i <= m; i++)
58 |         cin >> u >> v >> w[i], sum = (sum + w[i]) % P,
59 |         G[u].push_back({v, i}), G[v].push_back({u, i});
60 |     dfs1(1, 0);
61 |     for (int i = 1, j; i <= m; i++) if (he[i])
62 |         j = get(he[i]), ct[j]++, sw[j] = (sw[j] + w[i]) % P;
63 |     dfs2(1, 0, 0, 0);
64 |     for (int i = 1; i <= n; i++) cout << ans[i] << ' ';
65 | }

假设输入的整数满足 3≤n≤1053\le n\le10^5,2≤m≤2×1052\le m\le2\times10^5,0≤wi<9982443530\le w_i<998244353,且输入的图连通、无自环或重边,不考虑异或哈希值的冲突,完成下面的判断题和单选题。

第 28 题(判断题,1.5 分)

代码中使用了哈希表,攻击者可以在不获知随机数种子的前提下,通过构造数据,使得程序在该数据下的期望时间复杂度退化至平方级别。( )

正确答案错误

解析详情
答案 错误随机异或哈希与散列表
思路与推导
  1. 哈希表的键 `he[i]` 由未知的 64 位随机数异或得到;不知道随机种子时,攻击者无法预先把大量键定向到同一个 `key%400037` 桶。桶分布在期望意义下均匀,所以不能通过固定输入把期望复杂度构造成平方级。

第 29 题(判断题,1.5 分)

若对于 i≠ji\ne j有 `he[i]` 和 `he[j]` 相等且均不为 0,则图中存在一个环同时包含边i,ji,j。( )

正确答案正确

解析详情
答案 正确随机异或标记与环
思路与推导
  1. 每条返祖边产生一个随机标记,并把该标记异或到其基本环上的所有边。忽略哈希冲突时,`he[i]=he[j]≠0` 表示两边所属的基本环集合相同且非空;任选其中一个基本环,就得到同时包含边 i,ji,j 的简单环。

第 30 题(判断题,1.5 分)

若 m=n−1m=n-1,且对于 i<ni<n有ui=i,vi=i+1,wi=1u_i=i,v_i=i+1,w_i=1,则输出的所有数之和(在对 PP取模意义下)等于13n(n−1)(n−2)\frac13n(n-1)(n-2)。( )

正确答案正确

解析详情
答案 正确树上路径统计与求和
思路与推导
  1. 该图是一条边权全为 1 的树,到顶点 uu 时有 b=b2=u−1b=b2=u-1、sum=n−1sum=n-1、c=0c=0。故 ans[u]=(u−1)(n−1)−((u−1)2+u−1)/2ans[u]=(u-1)(n-1)-((u-1)^2+u-1)/2;令 u−1u-1 从 0 到 n−1n-1 求和,得到 n(n−1)(n−2)/3n(n-1)(n-2)/3。

第 31 题(单选题,3 分)

这份程序对于每个点 uu,求得了( )的答案对 PP 取模的结果。

A.
选择两条边 i<ji<j,满足存在一条 1 到 uu的路径pp,使得 i,ji,j中至少有一条边在pp上,所有方案的wi×wjw_i\times w_j 之和。
B.
选择两条边 i<ji<j,满足存在一条 1 到 uu的路径pp,使得 i,ji,j都在pp上,所有方案的wi×wjw_i\times w_j 之和。
C.
选择两条边 i<ji<j,满足对于所有 1 到 uu的路径pp,都有 i,ji,j中至少有一条边在pp上,所有方案的wi×wjw_i\times w_j 之和。
D.
选择两条边 i<ji<j,满足对于所有 1 到 uu的路径pp,都有 i,ji,j都在pp上,所有方案的wi×wjw_i\times w_j 之和。

正确答案C

解析详情
答案 C割边、二边割集与路径覆盖
思路与推导
  1. `b` 汇总 1 到 uu 的 DFS 树路径上的割边,任取其中一条与另一条边配对,所有 1 到 uu 的路径都至少经过前者。`c` 再统计同一非零 `he` 类中构成二边割集的边对;删去这两边会隔断 1 与 uu。因此统计条件是“每条路径至少含二者之一”,即 C。

第 32 题(单选题,3 分)

在 `dfs2` 函数中,若执行了分支 `if (ct[j] == 1) dfs2(v, c, b, b2)`,设当前正沿树边 ee向下遍历,则ee 在原图中满足?( )

A.
在原图中将边 ee 删去后,图的连通块数量必定会增加。
B.
在原图中将边 ee 删去后,图中必定会产生至少一条新的割边。
C.
边 ee 在原图中必然不属于任何一个简单环。
D.
在原图中将边 ee 删去后,图中不会产生任何新的割边。

正确答案D

解析详情
答案 D删边后的割边变化
思路与推导
  1. `he[i]≠0` 说明当前树边 ee 位于环上,本身不是割边;`ct[j]==1` 又说明没有另一条边与它组成相同的二边割类。因而删除 ee 后图仍连通,且不会有某条环边因只剩唯一通路而新变成割边。

第 33 题(单选题,3 分)

对于如下输入数据,输出的第 8 个数为?( )

10 10
1 2 10
2 3 20
3 4 30
4 5 40
5 6 10
6 7 10
7 8 10
8 9 10
9 10 10
10 5 10
A.
6900
B.
9500
C.
10400
D.
16900

正确答案C

解析详情
答案 C程序变量追踪与二边割统计
思路与推导
  1. 到顶点 8 的四条割边权为 10,20,30,4010,20,30,40,故 b=100,b2=3000b=100,b2=3000,基础项为 100×160−(10000+3000)/2=9500100\times160-(10000+3000)/2=9500。六条权为 10 的环边同属一类,走到 8 前三次增量依次为 500,300,100500,300,100,使 c=900c=900,故 ans[8]=9500+900=10400ans[8]=9500+900=10400。

三、完善程序(1)

(1)(线性筛)给定一个正整数 nn,要对于 i=1∼ni=1\sim n求出iii^i取模 998244353 的结果。满足1≤n≤1071\le n\le10^7。你需要设计一个 O(n)O(n) 的算法。

提示:在线性筛的过程中同步计算 fi=ii mod 998244353f_i=i^i\bmod998244353。质数可以直接使用快速幂;合数被筛到时,尝试将 fpqf_{pq}表示成fpf_p与fqf_q 的幂的乘积,从而由已有结果完成转移。

令 B=⌊n⌋B=\lfloor\sqrt n\rfloor。fpf_p的幂可将指数按BB 分块并预处理;fqf_q的幂利用对于枚举的qq,指数递增的性质,根据相邻质数之差增量维护。总时间复杂度即可达到 O(n)O(n)。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e7 + 5, SN = (int)sqrt(N) + 5, mod = 998244353;
int qpow(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = 1ull * res * a % mod;
        ①
        b >>= 1;
    }
    return res;
}
int bsgs1[SN][SN], bsgs2[SN][SN];
bool vis[N];
int f[N], pr[N / 10], len;
int powers[N], S;
int main() {
    int n;
    scanf("%d", &n);
    f[1] = 1;
    const int B = sqrt(n);
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            pr[++len] = i, f[i] = qpow(i, i);
            if (i <= B) {
                bsgs1[i][0] = 1;
                for (int j = 1; j <= B; j++)
                    bsgs1[i][j] = 1ull * bsgs1[i][j - 1] * f[i] % mod;
                bsgs2[i][0] = 1;
                for (int j = 1; j <= B; j++)
                    ②
            }
        }
        powers[0] = 1;
        int cur = 1, gap = 0;
        for (int j = 1; j <= len && i * pr[j] <= n; j++) {
            vis[pr[j] * i] = 1;
            int now = pr[j] - pr[j - 1];
            ③
                powers[ex] = 1ull * powers[ex - 1] * f[i] % mod;
            gap = max(gap, now);
            cur = 1ull * cur * powers[now] % mod;
            ④
            if (⑤)
                break;
        }
    }
    for (int i = 1; i <= n; i++)
        printf("%d ", f[i]);
    return 0;
}

第 34 题(单选题,3 分)

① 处应填( )。

A.
a = 1ull * a * a % mod;
B.
a = 1ull * a * b % mod;
C.
res = 1ull * a * a % mod;
D.
a = 1ull * res * res % mod;

正确答案A

解析详情
答案 A二进制快速幂
思路与推导
  1. 每轮处理完指数最低位后,指数执行 `b>>=1`,底数必须同步平方,才能让下一位对应 a2,a4,a8,…a^2,a^4,a^8,\ldots。因此应填写 `a = 1ull * a * a % mod;`。

第 35 题(单选题,3 分)

② 处应填( )。

A.
bsgs2[i][j] = 1ull * bsgs2[i][j - 1] * qpow(i, B) % mod;
B.
bsgs2[i][j] = 1ull * bsgs1[i][j - 1] * bsgs2[i][B - 1] % mod;
C.
bsgs2[i][j] = 1ull * bsgs1[i][j - 1] * qpow(f[i], B) % mod;
D.
bsgs2[i][j] = 1ull * bsgs2[i][j - 1] * bsgs1[i][B] % mod;

正确答案D

解析详情
答案 D分块预处理幂
思路与推导
  1. `bsgs1[i][j]=f[i]^j`,为了按 BB 分块表示指数,还需令 `bsgs2[i][j]=f[i]^{jB}`。由相邻块递推即乘上 `bsgs1[i][B]=f[i]^B`,所以填写 `bsgs2[i][j - 1] * bsgs1[i][B]`。

第 36 题(单选题,3 分)

③ 处应填( )。

A.
for (int ex = 1; ex <= now; ex++)
B.
for (int ex = gap + 1; ex <= now; ex++)
C.
for (int ex = 1; ex <= now; ex += B)
D.
for (int ex = gap + 1; ex <= now; ex += B)

正确答案B

解析详情
答案 B质数间隔的增量预处理
思路与推导
  1. `powers[ex]` 保存当前 `f[i]^ex`,`gap` 表示此前已经计算到的最大指数。当前质数差为 `now` 时,只需从 `gap+1` 补算到 `now`;若 `now<=gap`,循环自然不执行,因此应选 B。

第 37 题(单选题,3 分)

④ 处应填( )。

A.
f[pr[j] * i] = 1ull * bsgs2[pr[j] * i][i % B] * bsgs1[pr[j] * i][i / B] % mod * cur % mod;
B.
f[pr[j] * i] = 1ull * bsgs1[pr[j]][i % B] * bsgs2[pr[j]][i / B] % mod * cur % mod;
C.
f[pr[j] * i] = 1ull * bsgs2[pr[j]][i % B] * bsgs1[pr[j]][i / B] % mod * cur % mod;
D.
f[pr[j] * i] = 1ull * bsgs1[pr[j] * i][i % B] * bsgs2[pr[j] * i][i / B] % mod * cur % mod;

正确答案B

解析详情
答案 B完全积性分解与分块幂
思路与推导
  1. 对 p=pr[j]p=pr[j],有 f[pi]=(pi)pi=f[p]i⋅f[i]pf[pi]=(pi)^{pi}=f[p]^i\cdot f[i]^p。`cur` 由相邻质数差增量维护为 f[i]pf[i]^p;而 f[p]if[p]^i 按 i=(i mod B)+⌊i/B⌋Bi=(i\bmod B)+\lfloor i/B\rfloor B 拆成 `bsgs1[p][i%B] * bsgs2[p][i/B]`,故选 B。

第 38 题(单选题,3 分)

⑤ 处应填( )。

A.
pr[j] > i
B.
!(pr[j] % i)
C.
!(i % pr[j])
D.
i > pr[j]

正确答案C

解析详情
答案 CEuler 线性筛
思路与推导
  1. 线性筛要求每个合数只由其最小质因子筛到。当枚举质数 `pr[j]` 已整除 `i` 时,继续枚举会让后续合数重复生成,因此条件应为 `!(i % pr[j])`。

三、完善程序(2)

(2)(静态 Top Tree)

给定一棵由主链和若干叶子组成的带权树。主链包含顶点 1,2,…,n1,2,\ldots,n,对于 1≤i<n1\le i<n,顶点 ii与顶点i+1i+1之间有一条长度为aia_i的边。每个主链顶点还可能连接任意多条叶边;所有叶边按照输入顺序编号为1,2,…,k1,2,\ldots,k。共有 mm 次操作,每次操作为以下三种之一:

- `1 x y`:将主链边 axa_x的长度修改为yy; - `2 x y`:将编号为 xx的叶边长度修改为yy; - `3 l r`:询问由主链顶点 l,l+1,…,rl,l+1,\ldots,r 以及与它们相连的所有叶子组成的子树的直径长度。

其中,2≤n≤1052\le n\le10^5,0≤k≤1050\le k\le10^5,1≤m≤1051\le m\le10^5,所有边长均为不超过 10910^9 的非负整数。输入保证所有操作均合法。

输入的第一行包含三个整数 n,k,mn,k,m。第二行包含 n−1n-1个整数a1,a2,…,an−1a_1,a_2,\ldots,a_{n-1}。接下来的 nn行中,第ii行首先包含整数cic_i,随后包含 cic_i个整数,依次表示与顶点ii相连的叶边长度;保证∑ci=k\sum c_i=k。最后 mm 行每行包含一次操作。

为解决该问题,可以建立一棵静态 Top Tree。Top Tree 中的每个结点表示原树的一个连通子图,称为簇。一个顶点若属于该簇,同时还与不属于该簇的树边相连,则称为边界顶点。每个簇至多有两个边界。

静态 Top Tree 使用 rake(R)和 compress(C)两种合并。对于簇 xx,记 E(x)E(x) 为簇内边的集合,V(x)V(x)为边界顶点集合。边集不相交的簇a,ba,b只有在恰有一个公共边界,即∣V(a)∩V(b)∣=1|V(a)\cap V(b)|=1时才能合并,以保证结果仍为簇。rake 要求bb只有一个边界,compress 要求a,ba,b均有两个边界。若r,cr,c分别为两种合并的结果,则E(r)=E(c)=E(a)∪E(b)E(r)=E(c)=E(a)\cup E(b),V(r)=V(a)V(r)=V(a),V(c)=(V(a)∪V(b))∖(V(a)∩V(b))V(c)=(V(a)\cup V(b))\setminus(V(a)\cap V(b))。

Top Tree 上每个顶点代表的簇都是两个儿子顶点的簇以 R 或 C 方式合并得到。在本题中,算法先平衡合并每个主链顶点的叶簇,再将分支簇挂到对应主链边的左端,并在线段树中合并主链边簇。这样建立的 Top Tree 就可以在树上取出一个区间的簇回答询问。实现上,令簇信息 `Node` 维护边界距离 `w`、从左右边界出发的最远距离 `l` 和 `r`、簇内直径 `d` 及边界个数 `c`,即可实现合并。

以上算法的预处理复杂度为 O(n+k)O(n+k);每次操作复杂度为 O(log⁡n+log⁡k)O(\log n+\log k)。

#include <algorithm>
#include <iostream>
using namespace std;
const int N = 100005, M = 200005;
int n, k, m, z, tot, st[N], bel[N], id[N], lc[M], rc[M], fa[M];
long long a[N];
struct Node {
    long long w, l, r, d;
    int c;
} f[M], t[N << 2];
Node leaf(long long x) { return {0, x, x, x, 1}; }
Node path(long long x) { return {x, x, x, x, 2}; }
Node merge(Node a, Node b, char o, int s = 0) {
    Node z = a;
    if (o == 'R') {
        if (a.c == 1) {
            z.l = z.r = max(a.l, b.l);
        } else if (s == 0) {
            z.l = max(a.l, b.l); z.r = max(a.r, a.w + b.l);
        } else {
            z.l = max(a.l, a.w + b.l); z.r = max(a.r, b.l);
        }
        z.d = max(max(a.d, b.d), ①);
    } else {
        z.w = a.w + b.w; z.l = max(a.l, a.w + b.l);
        z.r = max(b.r, b.w + a.r);
        z.d = max(max(a.d, b.d), a.r + b.l); z.c = 2;
    }
    return z;
}
int join(int x, int y) {
    int u = ++tot; lc[u] = x; rc[u] = y; fa[x] = fa[y] = u;
    f[u] = merge(f[x], f[y], 'R'); return u;
}
int build_rake(int l, int r) {
    if (l == r) return l;
    int mid = (l + r) >> 1;
    return join(build_rake(l, mid), build_rake(mid + 1, r));
}
Node star(int x) { return st[x] ? f[st[x]] : leaf(0); }
Node make(int x) {
    Node y = path(a[x]);
    if (st[x]) ②;
    return y;
}
void build(int x, int l, int r) {
    if (l == r) { t[x] = make(l); return; }
    int mid = (l + r) >> 1;
    build(x << 1, l, mid); build(x << 1 | 1, mid + 1, r);
    t[x] = merge(t[x << 1], t[x << 1 | 1], 'C');
}
void change(int x, int l, int r, int q) {
    if (l == r) { t[x] = make(l); return; }
    int mid = (l + r) >> 1;
    if (q <= mid) change(x << 1, l, mid, q);
    else change(x << 1 | 1, mid + 1, r, q);
    t[x] = merge(t[x << 1], t[x << 1 | 1], 'C');
}
Node query(int x, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return t[x];
    int mid = (l + r) >> 1;
    if (qr <= mid) return query(x << 1, l, mid, ql, qr);
    if (ql > mid) return query(x << 1 | 1, mid + 1, r, ql, qr);
    Node p = query(x << 1, l, mid, ql, qr);
    Node q = query(x << 1 | 1, mid + 1, r, ql, qr);
    return ③;
}
void modify(int x, long long y) {
    int u = id[x]; f[u] = leaf(y);
    while (fa[u]) { u = fa[u]; f[u] = merge(f[lc[u]], f[rc[u]], 'R'); }
    if (④) change(1, 1, n - 1, bel[x]);
}
int main() {
    cin >> n >> k >> m;
    for (int i = 1; i < n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        int c, l = tot + 1; cin >> c;
        while (c--) {
            long long x; cin >> x; bel[++z] = i; id[z] = ++tot;
            f[tot] = leaf(x);
        }
        if (l <= tot) st[i] = build_rake(l, tot);
    }
    build(1, 1, n - 1);
    while (m--) {
        int o, x, y; cin >> o >> x >> y;
        if (o == 1) { a[x] = y; change(1, 1, n - 1, x); }
        else if (o == 2) modify(x, y);
        else {
            Node ans = ⑤;
            if (x < y) ans = merge(ans, star(y), 'R', 1);
            cout << ans.d << endl;
        }
    }
    return 0;
}

第 39 题(单选题,3 分)

① 处应填( )。

A.
(a.c == 1 || s == 1 ? a.l : a.r) + b.l
B.
(a.c == 1 ? max(a.d, b.d) : (s == 0 ? a.l : a.r)) + b.l
C.
(a.c == 1 || s == 0 ? a.l : a.w) + b.l
D.
(a.c == 1 || s == 0 ? a.l : a.r) + b.l

正确答案D

解析详情
答案 DTop Tree 的 rake 合并
思路与推导
  1. 簇 `b` 只有一个边界,跨越两簇的最长路径等于 `b.l` 加上簇 `a` 从公共边界出发的最远距离。`a.c==1` 或 `s==0` 时公共边界对应 `a.l`,否则对应 `a.r`,故交叉项为 `(a.c == 1 || s == 0 ? a.l : a.r) + b.l`。

第 40 题(单选题,3 分)

② 处应填( )。

A.
y = merge(y, star(x), 'R', 0);
B.
y = merge(y, star(x), 'R', 1);
C.
y = merge(star(x), y, 'R', 0);
D.
y = merge(y, star(x), 'C');

正确答案A

解析详情
答案 A主链簇与分支簇合并
思路与推导
  1. `path(a[x])` 表示主链边 (x,x+1)(x,x+1),`star(x)` 是挂在左端点 xx 的叶簇。应保持主链簇 `y` 为第一个参数,并以 `s=0` 在左边界执行 rake,所以是 `y = merge(y, star(x), 'R', 0);`。

第 41 题(单选题,3 分)

③ 处应填( )。

A.
merge(q, p, 'C')
B.
merge(p, q, 'R', 0)
C.
merge(p, q, 'R', 1)
D.
merge(p, q, 'C')

正确答案D

解析详情
答案 DTop Tree 的 compress 合并
思路与推导
  1. 线段树左侧查询结果 `p` 与右侧结果 `q` 是相邻的双边界主链簇,应按从左到右的顺序做 compress。故返回 `merge(p, q, 'C')`,这样新簇的左右边界和距离方向保持一致。

第 42 题(单选题,3 分)

④ 处应填( )。

A.
bel[x] < n
B.
x < n
C.
bel[x] <= n
D.
st[bel[x]] != 0

正确答案A

解析详情
答案 A更新传播范围
思路与推导
  1. 顶点 `bel[x]` 的叶簇只在 `make(bel[x])` 中并入其右侧主链边,而线段树只建立了边 11 到 n−1n-1。因此仅当 `bel[x]<n` 时需要调用 `change`;顶点 nn 的星簇在查询末端另行合并。

第 43 题(单选题,3 分)

⑤ 处应填( )。

A.
x == y ? leaf(0) : query(1, 1, n - 1, x, y - 1)
B.
x == y ? star(x) : query(1, 1, n - 1, x, y)
C.
x == y ? star(x) : query(1, 1, n - 1, x, y - 1)
D.
x == y ? star(x) : query(1, 1, n - 1, x + 1, y - 1)

正确答案C

解析详情
答案 C链区间与边区间映射
思路与推导
  1. 查询主链顶点区间 [x,y][x,y] 时,内部主链边恰为 x,x+1,…,y−1x,x+1,\ldots,y-1,所以 x<yx<y 时查询 `[x,y-1]`,随后再挂上 `star(y)`。当 x=yx=y 时没有主链边,答案簇就是 `star(x)`,故选 C。