试卷解析 · 理解每一步
洛谷 SCP-S 2025 第一轮模拟赛(提高级 C++)
CSPS-2025-R1-Mock-Luogu
试卷解析总览,可直接查看每题答案与解析。
第 1 题(单选题,2 分)
我国是人工智能大国,高度重视人工智能发展,积极推动互联网、大数据、人工智能和实体经济深度融合。下列关于人工智能领域的发展中,错误的说法是( )?
正确答案D
解析详情
我国通过“东数西算”等工程推动东西部算力协同,西部的能源和资源优势已被用于建设数据中心、承接算力需求。
因此“算力设施主要在东部且尚未影响西部产业”的说法与实际布局不符。
第 2 题(单选题,2 分)
你使用 g++ -O2 -o myapp main.cpp 命令编译了一个程序,然后尝试用 gdb ./myapp 进行调试。在 GDB 中,当你试图使用 print 命令查看某个局部变量的值时,最有可能遇到的情况是?( )
正确答案D
解析详情
命令只有 `-O2` 而没有 `-g`,可执行文件缺少局部变量所需的调试信息,变量还可能被优化掉,`print` 常会提示找不到符号或 `<optimized out>`。
GDB 不会因此崩溃,也不会自动重新编译程序,所以 A、B、C 均不准确。
第 3 题(单选题,2 分)
对于一个长度为 、每一项取值在 之间的正整数序列,下列关于快速排序、计数排序、堆排序最坏时间复杂度的组合,哪一个全部正确?( )
正确答案B
解析详情
普通快速排序在划分极不均衡时最坏为 ;计数排序要扫描 个元素并处理 个计数桶,为 。
堆排序建堆后进行 次对数级调整,最坏为 。
第 4 题(单选题,2 分)
使用 Kruskal 算法对一张连通图 求最小生成树,且要求时间复杂度不超过 。通常情况下,下列哪个数据结构是必要的?()
正确答案C
解析详情
Kruskal 按边权排序后依次尝试加入边,必须快速判断两个端点是否已在同一连通块。
并查集支持近常数均摊复杂度的查询与合并,使总复杂度由排序主导为 。
第 5 题(单选题,2 分)
对于以下 C++ 程序,假设 为输入给定的正整数,不考虑整型变量溢出和编译器优化,则程序的时间复杂度是( )?
int i = 1;
while (i < n) {
int j = n;
while (j > 0)
j /= 2;
i = i * 2;
}正确答案C
解析详情
外层每次令 翻倍,共执行 次;每次外层循环中,内层令 不断除以 2,也执行 次。
两层次数相乘得到 。
第 6 题(单选题,2 分)
对于一张无自环的有向图 G,如果每对不同结点之间恰有一条有向边,则称 G 为竞赛图。对于一张有 5 个结点的竞赛图 G,下列哪一个数字可能是出度为 0 的结点的个数?()
正确答案D
解析详情
若两个结点的出度都为 0,它们之间那条有向边必使其中一个结点出度至少为 1,因此出度为 0 的结点至多一个。
让其余 4 个结点的边全部指向某个结点即可出现恰好一个,故 1 是可能值。
第 7 题(单选题,2 分)
给定长度为 n 的正整数数组,使用前缀和与双指针算法,可以在线性时间解决哪一类问题?( )
正确答案A
解析详情
数组元素均为正数,右端点右移会使区间和增大,左端点右移会使区间和减小,两个指针都只单向移动。
配合前缀和或滑动窗口即可在线性时间统计和为 的子段。
第 8 题(单选题,2 分)
给定一个后缀表达式 3\ 4\ 2\ *\ 1\ 5\ -2\ 3\ ^\ ^/\ +(其中:^ 表示乘方,例如 ),则这个后缀表达式的值下取整的结果是?( )
正确答案B
解析详情
按栈计算得 、、,随后 。
最终值为 ,介于 3 与 4 之间,下取整为 3。
第 9 题(单选题,2 分)
假设 a, b, m 都是正整数,下列关于模运算的计算式中,恒成立的是( )?
正确答案A
解析详情
将 、 分别写成模 的商与余数,相加后的整倍数部分不影响最终余数,因此 恒成立。
除法不能直接对余数相除,乘法则应是相等而不是不等。
第 10 题(单选题,2 分)
某递归函数的递推式为 ,则其渐进时间复杂度为()。(在本题中,记 )
正确答案B
解析详情
递归项的临界指数为 ,而合并代价为 ,其指数更大。
并且 对某个 成立,故由主定理得 。
第 11 题(单选题,2 分)
某校要从5名教师和6名学生中选派5人组成一个活动小组,要求:小组中至少包含1名教师和2名学生,且教师甲和学生乙不能同时入选。请问共有()种不同的选派方案?
正确答案A
解析详情
先忽略甲、乙冲突:教师人数可为 1、2、3,方案数为 。
同时选甲、乙且仍满足条件的方案有 ,故答案为 。
第 12 题(单选题,2 分)
有一批正整数,均满足 ,现计划构造哈希函数 ,将元素放入 m 个桶内。若希望尽量覆盖全部的桶,以下哪一个 m 最合适?()
正确答案C
解析详情
所有数可写成 ,模 后可到达的桶数由步长 6 与 的最大公因数决定,共为 个。
与 6 互质,随着 变化可以遍历全部 97 个余数桶,覆盖最充分。
第 13 题(单选题,2 分)
在下列关于 Manacher 算法的描述中,对于一个长度为 N 的字符串 S,哪一个说法是正确的?( )
正确答案C
解析详情
Manacher 通常在相邻字符及首尾加入分隔符,把奇、偶长度回文统一为奇长度回文处理。
它利用已知回文区间的对称性在线性时间扩展,不依赖分治、普通动态规划或 KMP 的 border 数组。
第 14 题(单选题,2 分)
对于有向图 ,定义 为将 忽略方向得到的无向图。已知点集 ,边集 ,其中 表示 连向 的有向边。关于 和 的说法中,正确的是( )?
正确答案D
解析详情
有向边均由 指向 ,显然无法从偶数结点回到奇数结点,所以 不强连通。
忽略方向后恰为六边形环 ;偶环是二分图,且删去任一顶点后仍连通,因此点双连通。
第 15 题(单选题,2 分)
有 8 个布尔变量 满足下列异或方程( 表示异或):
满足全部方程的不同解的数量为多少?( )
正确答案B
解析详情
由第 5、2、4 式依次得 、、;代回第 1 式得 ,再由第 3 式得 。
可独立取值,共有 组解。
二、阅读程序(1)
#include <iostream>
using namespace std;
const int maxn = 10000000 + 7;
int n, f[maxn], g[maxn];
long long m, ans = 1;
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> f[i];
}
for (int i = 1; i <= n; i++) {
if (f[i] == 1)
for (int j = i; j <= n; j += i)
g[j] = 1;
if (g[i] == 0)
ans = ans * i % m;
}
cout << ans << endl;
return 0;
}第 16 题(判断题,1.5 分)
该程序输出一行一个不小于 0 而小于 m 的整数。()
正确答案错误
解析详情
当 时, 被置为 1,乘法语句不会执行,`ans` 保持初值 1。
程序输出 1,而“不小于 0 且小于 ”此时只能是 0,因此断言错误。
第 17 题(判断题,1.5 分)
删去读入循环末尾的右花括号 `}`,并删去紧随其后的第二个 `for (int i = 1; i <= n; i++) {`,程序仍然能正常运行,且输出结果不变。( )
正确答案正确
解析详情
删除读入循环末尾的右花括号和第二个 `for` 声明后,处理逻辑被并入读入循环:每次读入 `f[i]` 后立即处理同一个 。
处理 时只依赖当前及更小下标产生的 `g`,不需要未来的 `f`,所以结果不变。
第 18 题(判断题,1.5 分)
将处理循环中的 `if (f[i] == 1)` 改为 `if (f[i] == 1 && g[i] == 0)`,程序仍然能正常运行,且输出结果不变。( )
正确答案正确
解析详情
若处理到 时已有 ,必存在更小的 且 ;所有 的倍数也都是 的倍数,早已被标记。
因此此时跳过 的内层循环不会改变任何 `g[j]`,也不会改变乘积。
第 19 题(单选题,3 分)
该程序(对于 n)的最坏情况下的时间复杂度是( )?
正确答案C
解析详情
最坏情况下所有 ,第 次内层循环访问约 个倍数,总次数为 。
该和为 ,外层的 不改变数量级。
第 20 题(单选题,3 分)
对于以下输入,程序输出结果除以 13 的余数是( )?
31 10000
0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0正确答案B
解析详情
;处理 2 时所有偶数已被标记,处理 4 只重复标记,所以最终只把 乘入 `ans`。
奇数连乘逐步模 10000 的结果为 625,而 。
二、阅读程序(2)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1010;
int n, m, mod;
long long ans, y[N], f[N], g[N], p[N], s[N];
long long qmi(long long a, int b, int p) {
long long res = 1;
while (b) {
if (b & 1)
res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
long long solve(int n, int k) {
memset(y, 0, sizeof(y));
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
memset(p, 0, sizeof(p));
memset(s, 0, sizeof(s));
long long ans = 0;
for (int i = 1; i <= k + 2; i++)
y[i] = (y[i - 1] + qmi(i, k, mod)) % mod;
f[0] = g[0] = 1;
for (int i = 1; i <= k + 2; i++)
f[i] = f[i - 1] * i % mod;
for (int i = 1; i <= k + 2; i++)
g[i] = -g[i - 1] * i % mod;
p[0] = s[k + 3] = 1;
for (int i = 1; i <= k + 2; i++)
p[i] = p[i - 1] * (n - i) % mod;
for (int i = k + 2; i >= 1; i--)
s[i] = s[i + 1] * (n - i) % mod;
for (int i = 1; i <= k + 2; i++) {
long long t1 = p[i - 1] * s[i + 1] % mod;
long long t2 = f[i - 1] * g[abs(k - i + 2)] % mod;
(ans += y[i] * t1 % mod * qmi(t2, mod - 2, mod) % mod) %= mod;
ans = (ans + mod) % mod;
}
return ans;
}
int main() {
scanf("%d%d%d", &n, &m, &mod);
long long res = 0;
for (int i = 1; i <= n; i++) {
res += (n - i + 1) * qmi(m, n - i, mod) % mod * solve(m, i);
res %= mod;
}
printf("%lld", res);
return 0;
}第 21 题(判断题,1.5 分)
qmi 函数实现了快速幂算法。该函数对于任意不超过 的非负整数 ,以及不超过 的正整数 ,均能正确返回 对 取模的值。( )
正确答案错误
解析详情
循环确实是二进制快速幂,但当 时循环不执行,函数直接返回初值 1。
正确的 应为 0,所以“对任意给定范围均正确”不成立。
第 22 题(判断题,1.5 分)
删除程序的第23到27行,程序仍然能正常运行,且输出结果不变。()
正确答案正确
解析详情
这些数组是全局数组,首次调用前已经全为 0;每次 `solve` 又会重设 `f[0],g[0],p[0],s[k+3]`,并覆盖本次实际读取的其余下标。
删除五次 `memset` 后没有旧值进入当前计算,输出不变。
第 23 题(判断题,1.5 分)
当输入的 m 为 1 时,输出总为 。()
正确答案错误
解析详情
当 时,`solve(1,i)=1`,主循环未取模前的和确为 。
但程序每轮执行 `res %= mod`,实际输出是该值对 `mod` 取模的结果,不保证等于原整数。
第 24 题(单选题,3 分)
这份程序的最坏时间复杂度是()。
正确答案D
解析详情
第 次主循环调用 `solve(m,i)`,其中 次幂运算的指数分别为 或 ,耗时 ;外层还各有一次 快速幂。
对 求和得到 。
第 25 题(单选题,3 分)
对于以下的输入数据,输出结果为( )。
3 4 10331
正确答案C
解析详情
长度为 的固定子段,在所有序列中的最小值总和为 ,且共有 个位置。
的贡献依次为 、、,合计 820。
第 26 题(单选题,4 分)
这份程序求得了( )的答案对 \mod 取模的结果。
正确答案C
解析详情
`solve(m,i)` 用拉格朗日插值求 ;对一个长度为 的子段,所有取值方案的最小值总和正是 。
乘上子段位置数 和其余位置方案数 ,再对 求和,得到选项 C。
二、阅读程序(3)
#include <iostream>
#include <string>
typedef long long ll;
const int alpha = 32;
const int maxn = 10000 + 5, maxk = 100 + 5;
const int maxv = 100000 + 5, maxs = 100000 + 5;
const ll mod = 1000000000 + 7;
using namespace std;
int n, k, v;
ll a[maxv];
string s;
int tr[maxs][alpha], dep[maxs], cnt[maxs];
int nxt[maxs], cnt1;
int id[maxs], cnt2;
ll c[maxn][maxk];
ll dp[maxn * 2][maxk], temp[maxk];
int siz[maxs];
void dfs(int ver) {
int numb = 0, son = 0;
for (int i = 0; i < alpha; i++) {
if (tr[ver][i] != 0) {
dfs(tr[ver][i]);
++numb;
son = tr[ver][i];
}
}
if (ver != 1 && numb == 1 && cnt[ver] == 0) {
nxt[ver] = nxt[son];
} else {
nxt[ver] = ver;
id[ver] = ++cnt2;
}
}
void solve(int ver) {
ll f = 1, g = 1;
siz[ver] = cnt[ver];
for (int i = 0; i <= siz[ver] && i <= k; i++) {
dp[id[ver]][i] = f * c[siz[ver]][i] % mod;
f = f * g % mod;
g = g * a[dep[ver]] % mod;
}
for (int i = 0; i < alpha; i++)
if (tr[ver][i] != 0)
solve(nxt[tr[ver][i]]);
for (int i = 0; i < alpha; i++)
if (tr[ver][i] != 0) {
int x = nxt[tr[ver][i]];
for (int j = 0; j <= k && j <= siz[ver] + siz[x]; j++)
temp[j] = 0;
ll value1 = 1;
for (int j1 = 0; j1 <= k && j1 <= siz[ver]; j1++) {
ll value2 = dp[id[ver]][j1];
for (int j2 = 0; j2 <= siz[x] && j1 + j2 <= k; j2++) {
temp[j1 + j2] = (temp[j1 + j2] +
value2 * dp[id[x]][j2]) % mod;
value2 = value2 * value1 % mod;
}
value1 = value1 * a[dep[ver]] % mod;
}
siz[ver] += siz[x];
for (int j = 0; j <= siz[ver] && j <= k; j++)
dp[id[ver]][j] = temp[j];
}
}
int main() {
cin >> n >> k >> v;
for (int i = 0; i <= v; i++)
cin >> a[i];
cnt1 = 1;
for (int i = 1; i <= n; i++) {
cin >> s;
int cur = 1, len = s.size();
for (int j = 0; j < len; j++) {
int ch = s[j] & 31;
if (tr[cur][ch] == 0) {
tr[cur][ch] = ++cnt1;
dep[cnt1] = dep[cur] + 1;
}
cur = tr[cur][ch];
}
++cnt[cur];
}
c[0][0] = 1;
for (int i = 1; i <= n; i++) {
c[i][0] = 1;
for (int j = 1; j <= k && j <= i; j++)
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
}
dfs(1);
solve(1);
cout << dp[id[1]][k] << endl;
return 0;
}第 27 题(判断题,1.5 分)
若输入的所有字符串长度均不小于2,且它们的第一个字符均相同,第二个字符两两不同,且 ,则输出的结果为 。()
正确答案正确
解析详情
任意两串首字符相同而第二字符不同,所以它们的最长公共前缀长度都恰为 1;程序给每对被选字符串乘上 。
因 ,每个大小为 的子集权值均为 1,`dp[id[1]][k]` 就是 。
第 28 题(判断题,1.5 分)
将第5行的 const int alpha = 32; 改为 const int alpha = 26; 后,程序仍然能正常运行,且输出结果不变。()
正确答案错误
解析详情
小写字母通过 `s[j] & 31` 映射到 ,其中字符 `z` 的下标为 26。
若第二维长度改为 26,合法下标只有 ,访问 `tr[*][26]` 会越界,结果无法保证。
第 29 题(判断题,2 分)
若将第18行的 ll dp[maxn * 2][maxk] 改为 ll dp[maxs][maxk],第30行的if (ver != 1 && numb == 1 && cnt[ver] == 0) 改为 if (false),程序仍然能正常运行,且输出结果不变。()
正确答案正确
解析详情
取消压缩后,单儿子且非终止的结点也进入 `solve`;该结点有 `cnt=0`,自身只含 `dp[0]=1`,与唯一儿子合并时不会增加任何权值,因而只是原样传递儿子 DP。
`dp[maxs][maxk]` 又足以为所有至多 个 Trie 结点分配状态,所以输出不变。
第 30 题(单选题,3 分)
假设 alpha 为常数,那么该程序的最坏时间复杂度是( )。
正确答案D
解析详情
读入权值、建 Trie 和路径压缩分别为 、、;组合数表计算到 为 。
压缩后保留的终止/分叉结点为 ,各子树合并的截断卷积按所含字符串数摊还为 ,故总计 。
第 31 题(单选题,4 分)
记 为满足在程序运行结束时满足恰有 26 个 满足 的 的个数。若在本题条件下,记 的最大值为 ,而在满足本题条件且 时,记此时 的最大值为 。设 ,则 除以 17 的余数是( )。
正确答案B
解析详情
每增加一个拥有 26 个儿子的结点,最少使叶子数增加 25;由 得 ,且平衡构造的总长度不超过 。
时按最浅叶子优先展开:根贡献 26,继续展开 26 个深度 1 叶子后总长度为 1352;再展开深度 2 叶子每次增加 76,故还能展开 个,。
所以 ,而 。
第 32 题(单选题,3 分)
对于以下的输入数据,程序输出的结果是()。
10 3 12
3 1 4 6 5 2 0 3 6 1 4 2 5
ak firstround goodluck havefun luogu luogudevteam luotianyi rpplusplus scp scps
正确答案C
解析详情
建 Trie 并压缩单链后,根的有效儿子按字符为 `a,f,g,h,l,r,s`,对应子树字符串数为 。
依次合并这些子树时,根状态 `dp[3]` 变为 ;其中跨子树选择按当前结点深度的 累乘。
最终 `dp[id[1]][3]=3816`。
三、完善程序(1)
(1)(树的重心)给定一颗树,树中包含 个结点(编号)和 条无向边。请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。上述参数满足。试补全程序。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
int n, pos, maxx_part = 0x3f3f3f3f, siz[N], v[N];
int h[N], e[N], ne[N], idx;
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
①;
}
void dfs(int x) {
v[x] = 1;
②;
int max_part = 0;
for (int i = h[x]; ~i; i = ne[i]) {
int j = e[i];
if (v[j]) continue;
dfs(j);
siz[x] += siz[j];
max_part = max(max_part, siz[j]);
}
③;
maxx_part = min(maxx_part, max_part);
}
int main() {
④;
cin >> n;
for (int i = 0; i < n; i++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(1);
cout << ⑤;
return 0;
}第 33 题(单选题,3 分)
①处应填( )
h[a] = ne[idx + 1]h[a] = e[idx + 1]h[a] = ++idxh[a] = idx++正确答案D
解析详情
新边编号为当前 `idx`,`e[idx]=b` 保存终点,`ne[idx]=h[a]` 接到原链表头之后,应令 `h[a]=idx` 再把 `idx` 加 1。
表达式 `h[a] = idx++` 正好完成这两个动作。
第 34 题(单选题,3 分)
②处应填( )
siz[x] = -1siz[x] = 0siz[x] = 1siz[x] = 0x3f3f3f3f正确答案C
解析详情
`siz[x]` 表示以 为根的子树结点数,进入 `dfs(x)` 时必须先计入结点 本身,因此初始化为 1。
随后再把每个未访问儿子的 `siz[j]` 累加进去。
第 35 题(单选题,3 分)
③处应填( )
max_part = max(max_part, n - siz[x])maxx_part = max(maxx_part, n - siz[x])max_part = max(max_part, siz[x])maxx_part = max(maxx_part, siz[x])正确答案A
解析详情
删除 后,儿子方向连通块大小已由 `siz[j]` 更新到 `max_part`;父亲方向剩余的连通块大小为 。
所以还需执行 `max_part = max(max_part, n - siz[x])`,才能得到删除 后最大连通块。
第 36 题(单选题,3 分)
④处应填()。
memset(h, 0, sizeof(h))memset(h, 63, sizeof(h))memset(h, -63, sizeof(h))memset(h, 255, sizeof(h))正确答案D
解析详情
遍历条件 `~i` 要在 时停止,因此每个 `h[a]` 初始值必须为 -1。
`memset(h,255,sizeof(h))` 把每个字节写成 `0xFF`,在常用二进制补码表示下整数值即为 -1。
第 37 题(单选题,3 分)
⑤处应填( )。
siz[maxx_part == 2e9 ? 0 : maxx_part]maxx_partmaxx_part + 12 * maxx_part - 1正确答案B
解析详情
对每个结点计算删除后的最大连通块大小 `max_part`,再用 `maxx_part=min(maxx_part,max_part)` 维护全局最小值。
题目要求输出的正是这个最小的最大连通块大小,因此输出 `maxx_part`。
三、完善程序(2)
(2)(矩形覆盖问题)在平面直角坐标系上有 个矩形,矩形的每条边与坐标系的横轴平行或垂直。对于第个矩形,矩形左下角的顶点坐标为,右上角的顶点坐标为 。每个矩形要么严格包含另一个矩形,要么完全不相交。矩形的边界不会相交、顶点或边也不会重合。要求,对于每个矩形,分别求出包含它的矩形中最小的矩形。如果没有被覆盖,则输出 。上述参数满足 ,,你需要设计一个时间复杂度不超过 的程序。试补全程序。
#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
struct rectangle {
ll sx, sy, ex, ey;
};
struct line {
ll x, sy, ey, id;
friend bool operator < (const line &lhs, const line &rhs) {
return ①;
}
};
struct segmentTree {
vector<int> tree, lazy;
static int ls(int u) { return u << 1; }
static int rs(int u) { return u << 1 | 1; }
segmentTree(int N): tree(N * 4 + 1, 0), lazy(N * 4 + 1, -1) {}
void pushdown(int p) {
if (~lazy[p]) {
tree[ls(p)] = tree[rs(p)] = lazy[p];
lazy[ls(p)] = lazy[rs(p)] = lazy[p];
lazy[p] = -1;
}
}
void update(int p, int l, int r, int ql, int qr, int k) {
if (l >= ql && r <= qr) {
tree[p] = lazy[p] = k;
return;
}
pushdown(p); int mid = (l + r) >> 1;
if (②) update(ls(p), l, mid, ql, qr, k);
if (qr > mid) update(rs(p), mid + 1, r, ql, qr, k);
}
int query(int p, int l, int r, int x) {
if (l == r) return tree[p];
pushdown(p); int mid = (l + r) >> 1;
if (mid >= x) return query(ls(p), l, mid, x);
else return query(rs(p), mid + 1, r, x);
}
};
template<class T> struct discretize {
vector<T> data;
discretize(): data() {}
void insert(const T &x) { data.push_back(x); }
void build() {
sort(data.begin(), data.end());
auto endp = unique(data.begin(), data.end());
data.erase(endp, data.end());
}
size_t size() const { return data.size(); }
int operator[](const T &x) {
return ③;
}
};
int main() {
int n; cin >> n;
vector<rectangle> P(n);
for (int i = 0; i < n; i++)
cin >> P[i].sx >> P[i].sy >> P[i].ex >> P[i].ey;
discretize<ll> discx, discy;
for (int i = 0; i < n; i++) {
discx.insert(P[i].sx);
discx.insert(P[i].ex);
discy.insert(P[i].sy);
discy.insert(P[i].ey);
}
discx.build(); discy.build();
for (int i = 0; i < n; i++) {
P[i].sx = discx[P[i].sx] + 1;
P[i].ex = discx[P[i].ex] + 1;
P[i].sy = discy[P[i].sy] + 1;
P[i].ey = discy[P[i].ey] + 1;
}
vector<line> L; int cnt = 0;
for (int i = 0; i < n; i++) {
L.push_back({P[i].sx, P[i].sy, P[i].ey, ++cnt});
L.push_back({④});
}
sort(L.begin(), L.end());
int N = discx.size();
segmentTree S(N);
vector<int> fa(n + 1, -1);
for (int i = 0; i < L.size(); i++) {
int c = S.query(1, 1, N, L[i].sy);
if (L[i].id < 0)
S.update(1, 1, N, L[i].sy, L[i].ey, fa[-L[i].id]);
else {
fa[L[i].id] = c;
⑤
}
}
for (int i = 1; i <= cnt; ++i)
cout << fa[i] << " \n"[i == cnt];
return 0;
}第 38 题(单选题,3 分)
①处应填( )
lhs.sy < rhs.sy || lhs.ey < rhs.eylhs.sy == rhs.sy ? lhs.id < rhs.id : lhs.sy < rhs.sylhs.ey == rhs.ey ? lhs.id < rhs.id : lhs.ey < rhs.eylhs.x == rhs.x ? lhs.id < rhs.id : lhs.x < rhs.x正确答案D
解析详情
每条竖边事件首先必须按横坐标 `x` 从小到大处理;同一横坐标时再按 `id` 排序,使负编号的离开事件先于正编号的进入事件。
因此比较式为 `lhs.x == rhs.x ? lhs.id < rhs.id : lhs.x < rhs.x`。
第 39 题(单选题,3 分)
②处应填( )
ql < midql <= midql >= midql == mid正确答案B
解析详情
当前结点左儿子覆盖 ;待修改区间 与它相交的条件是 。
配合已有的 `qr > mid` 判断即可分别递归到所有相交儿子。
第 40 题(单选题,3 分)
③处应填( )
lower_bound(data.begin(), data.end(), x) - data.begin()upper_bound(data.begin(), data.end(), x) - data.begin()find(data.begin(), data.end(), x) - data.begin()distance(data.end(), upper_bound(data.begin(), data.end(), x))正确答案A
解析详情
`build` 后 `data` 已排序去重,且传入的 一定来自已插入坐标。
`lower_bound(data.begin(),data.end(),x)-data.begin()` 返回 的零基排名,主程序再加 1 转成线段树的一基下标。
第 41 题(单选题,3 分)
④处应填( )
P[i].sx, P[i].sy, P[i].ey, cntP[i].sx, P[i].sy, P[i].ey, -cntP[i].ex, P[i].sy, P[i].ey, cntP[i].ex, P[i].sy, P[i].ey, -cnt正确答案D
解析详情
左边界 使用正编号表示矩形进入,右边界必须在 处使用相反的负编号表示矩形离开。
纵向覆盖范围在两次事件中都保持为 ,故应填 `P[i].ex, P[i].sy, P[i].ey, -cnt`。
第 42 题(单选题,3 分)
⑤处应填()
fa[i] = S.query(1, 1, N, c)fa[i] = S.query(1, 1, N, L[i].id)S.update(1, 1, N, L[i].sy, L[i].ey, L[i].id)S.update(1, 1, N, L[i].sx, L[i].ex, L[i].id)正确答案C
解析详情
正编号事件表示进入矩形:先查询 `sy` 处当前最内层覆盖编号并记为 `fa[id]`,再把整个 区间赋成当前矩形编号。
因此应调用 `S.update(1,1,N,L[i].sy,L[i].ey,L[i].id)`;离开时程序再用 `fa[-id]` 恢复父矩形。