#csps2. csps2
csps2
一、单项选择题(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)
- 在 Linux 终端中依次执行如下命令:
下列说法正确的是(){{ select(1) }}chmod u+x run.sh ./run.sh < input.txt > output.txt 2> error.log
chmod u+x run.sh会给所有用户增加执行权限./run.sh表示执行当前目录下的run.sh;标准输入来自input.txt,标准输出写入output.txt,标准错误写入error.log2> error.log会把标准输出和标准错误都写入error.log< input.txt表示把程序输出追加到input.txt末尾
- 某程序使用如下比较器定义优先队列:
若依次插入struct Node { int d, id; }; struct Cmp { bool operator()(const Node& a, const Node& b) const { if (a.d != b.d) return a.d > b.d; return a.id < b.id; } };{3,1}、{2,5}、{2,4}、{3,9},下列说法正确的是(){{ select(2) }}
top()优先取d最大者;若d相同,取id最小者- 弹出顺序为
{2,5}、{2,4}、{3,9}、{3,1} - 弹出顺序为
{2,4}、{2,5}、{3,1}、{3,9} - 比较器返回
true表示a应当排在堆顶,因此{3,9}最先弹出
- 有向带权图有 5 个点,边为:
从点 1 出发,按 Dijkstra 算法同时统计最短路条数。到点 5 的最短距离与最短路条数分别为(){{ select(3) }}1->2(2), 1->3(1), 3->2(1), 2->4(2), 3->4(4), 2->5(3), 4->5(1), 3->5(6)
4, 25, 35, 46, 4
- 某开放寻址散列表长度为 11,散列函数为
h(x)=x mod 11,冲突时使用线性探查。依次插入22, 1, 13, 24后,再查找不存在的35,随后将35插入表中。下列说法正确的是(){{ select(4) }}
- 查找
35访问 2 个槽,35插入下标 4,插入后装载因子为5/11 - 查找
35访问 3 个槽,35插入下标 5,插入后装载因子为5/11 - 查找
35访问 3 个槽,35插入下标 4,插入后装载因子为5/11 - 查找
35访问 3 个槽,35插入下标 4,插入后装载因子为4/11
- 某前缀编码需要编码 7 个字符,其出现频率分别为:
若使用哈夫曼编码,则最小带权路径长度(WPL)与最长编码长度分别为(){{ select(5) }}3, 5, 6, 8, 12, 15, 20
180, 4180, 3172, 469, 4
- 对于如下差分约束系统:
将约束x2 - x1 <= 3 x3 - x2 <= 2 x1 - x3 <= -6xv - xu <= w看作有向边u -> v,边权为w。下列说法正确的是(){{ select(6) }}
- 系统有解,因为所有边权之和为负
- 系统无解,因为存在权值和为
3+2-6=-1的负环 - 系统有解,例如
x1=x2=x3=0 - 只要加入超级源点,差分约束系统一定有解
- 数组
a=[8,4,6,2,7,3,5,1,9,2,6,4]的所有连续子数组最小值之和为(){{ select(7) }}
- 152
- 158
- 161
- 168
- 字母表为
{a,b,c}。若一个长度为 5 的字符串以集合P中任意一个字符串为前缀,则称它被禁止。集合P为:
把a, ab, ba, cab, bc, cc, aab, bcaP插入 Trie 后进行剪枝:若某个前缀已经是终止节点,则它的所有后代终止节点都不再保留。问未被禁止的长度为 5 的字符串个数,以及剪枝后 Trie 的节点总数(含根节点)分别为(){{ select(8) }}
63, 972, 881, 972, 9
- 并查集初始有 9 个单点集合,依次合并:
若每次成功合并两个不同集合时,把当前图中"处在同一连通块内的无序点对数"增加(1,2), (2,3), (4,5), (6,7), (5,6), (3,7), (8,9), (1,9), (2,8)size(A)*size(B),其中A,B为合并前两个连通块;若两点已在同一集合中,则本次合并不改变点对数。最终该点对数与成功合并次数分别为(){{ select(9) }}
35, 836, 836, 937, 8
- 阅读下面程序片段:
设void work(int l, int r) { if (l == r) return; int mid = (l + r) >> 1; vector<int> v; for (int i = l; i <= r; ++i) v.push_back(a[i]); sort(v.begin(), v.end()); work(l, mid); work(mid + 1, r); }n为 2 的幂,数组a[1..n]已经给定,调用work(1,n),且不考虑输入输出开销。该程序片段的时间复杂度与额外空间复杂度分别为(){{ select(10) }}
- 无向图边集为:
关于欧拉路,下列说法正确的是(){{ select(11) }}1-2, 2-3, 3-1, 3-4, 4-5, 5-6, 6-4, 6-7, 7-8, 8-4
- 图不连通,因此不存在欧拉路
- 有欧拉回路,因为所有点度数均为偶数
- 有欧拉路但无欧拉回路;若再加入边
3-6,则有欧拉回路 - 只有有向图才讨论欧拉路
- 有向图有 7 个点,边为:
用 Floyd 传递闭包求所有可达关系,初始化时令1->2, 1->3, 2->4, 2->6, 3->4, 4->5, 5->3, 5->6, 6->7reach[i][i]=true。若最终统计时不计自达关系,则可达有序点对(u,v)的个数,以及满足1能到达x且x能到达7的点x个数分别为(){{ select(12) }}
24, 723, 625, 724, 6
- 由 8 个字符
A和 5 个字符B组成一个长度为 13 的字符串。要求该字符串的任意一个前缀中,A的个数都不少于B的个数。满足条件的字符串共有()个{{ select(13) }}
- 429
- 572
- 715
- 1287
- 在
0到200的整数中,十进制表示不含数字4,且各位数字和为偶数的整数共有()个{{ select(14) }}
- 80
- 82
- 84
- 90
- 一堆石子每次可以取走
1、3或4个,不能操作者负。现在有三堆石子,数量分别为7,8,10。若用SG(n)表示一堆n个石子的 SG 函数值,则下列说法正确的是(){{ select(15) }}
SG(7)=1, SG(8)=0, SG(10)=1,先手必胜SG(7)=0, SG(8)=1, SG(10)=1,先手必胜SG(7)=0, SG(8)=1, SG(10)=1,先手必败SG(7)=2, SG(8)=3, SG(10)=1,先手必败
二、阅读程序(程序输入不超过数组或字符串定义的范围;判断题正确填对,错误填错;除特殊说明外,判断题1.5分,选择题3分,标注分值题按标注计分,共计40分)
(1)
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1000000007LL;
int main() {
string s;
cin >> s;
int n = s.size();
vector<int> pi(n, 0);
for (int i = 1; i < n; ++i) {
int j = pi[i - 1];
while (j > 0 && s[i] != s[j]) j = pi[j - 1];
if (s[i] == s[j]) ++j;
pi[i] = j;
}
int p = n - pi[n - 1];
int rep = (n % p == 0 ? n / p : 1);
vector<int> borders;
for (int x = pi[n - 1]; x > 0; x = pi[x - 1]) {
borders.push_back(x);
}
long long code = 0;
for (int x : borders) code = (code * 131 + x) % MOD;
cout << pi[n - 1] << " " << p << " " << rep << "\n";
cout << borders.size() << " " << code << "\n";
return 0;
}
给定输入:
ababababa
pi[i]表示s[0..i]的最长真前缀与真后缀相等的长度。(){{ select(16) }}
- 对
- 错
- 对给定输入,
borders中的元素依次为7,5,3,1。(){{ select(17) }}
- 对
- 错
- 若
n % p != 0,则p一定不是该字符串的周期长度。(2分)(){{ select(18) }}
- 对
- 错
- 对给定输入,程序输出为(3分){{ select(19) }}
-
7 2 1 4 15822836 -
7 2 4 4 15822836 -
5 2 1 3 657 -
7 1 9 4 15822836
- 若输入为
abcababcab,程序第一行输出为(2分){{ select(20) }}
5 2 15 5 22 5 15 5 1
- 若输入为
aaaaaa,程序第二行输出为(3分){{ select(21) }}
5 156 4815437085 4815437085 657
(2)
#include <bits/stdc++.h>
using namespace std;
int n;
long long W;
vector<long long> a;
void gen(int l, int r, vector<long long>& out) {
int m = r - l;
for (int mask = 0; mask < (1 << m); ++mask) {
long long sum = 0;
for (int i = 0; i < m; ++i) {
if (mask & (1 << i)) sum += a[l + i];
}
if (sum <= W) out.push_back(sum);
}
}
int main() {
cin >> n >> W;
a.resize(n);
for (int i = 0; i < n; ++i) cin >> a[i];
int mid = n / 2;
vector<long long> L, R;
gen(0, mid, L);
gen(mid, n, R);
sort(R.begin(), R.end());
long long cnt = 0, best = 0;
for (long long x : L) {
vector<long long>::iterator it = upper_bound(R.begin(), R.end(), W - x);
cnt += it - R.begin();
if (it != R.begin()) {
--it;
best = max(best, x + *it);
}
}
cout << cnt << "\n";
cout << best << "\n";
return 0;
}
给定输入:
8 17
3 8 4 6 5 7 2 9
gen(l,r,out)枚举的是下标区间[l,r)内所有子集的元素和,并且只保留不超过W的和。(){{ select(22) }}
- 对
- 错
- 若右半部分有两个不同子集的和相同,程序会把它们分别计入答案。(){{ select(23) }}
- 对
- 错
- 若把
upper_bound(R.begin(), R.end(), W - x)改成lower_bound(R.begin(), R.end(), W - x),程序输出一定不变。(2分)(){{ select(24) }}
- 对
- 错
- 对给定输入,程序输出为(3分){{ select(25) }}
-
78 17 -
77 17 -
78 16 -
65 16
- 对给定输入,
gen调用完成后,L.size()与R.size()分别为(3分){{ select(26) }}
13, 1314, 1314, 1416, 16
- 若仅把给定输入中的
W改为 16,则程序输出为(3分){{ select(27) }}
-
68 16 -
78 17 -
65 16 -
68 15
(3)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 65;
bitset<MAXN> reach[MAXN];
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) reach[i][i] = 1;
for (int i = 0; i < m; ++i) {
int u, v;
cin >> u >> v;
reach[u][v] = 1;
}
for (int k = 1; k <= n; ++k) {
for (int i = 1; i <= n; ++i) {
if (reach[i][k]) reach[i] |= reach[k];
}
}
int pairs = 0, center = 0;
for (int i = 1; i <= n; ++i) pairs += (int)reach[i].count() - 1;
for (int x = 1; x <= n; ++x)
if (reach[1][x] && reach[x][n]) ++center;
long long mask1 = 0;
for (int i = 1; i <= n; ++i)
if (reach[1][i]) mask1 += (1LL << i);
int code = 0;
for (int i = 1; i <= n; ++i)
code = (code * 97 + (int)reach[i].count()) % 100000;
cout << pairs << " " << center << "\n";
cout << mask1 << " " << code << "\n";
return 0;
}
给定输入:
6 7
1 2
1 3
2 4
3 4
4 5
2 6
5 6
- 程序把每个点到自身的可达关系也设为真,但
pairs统计时会减去自达关系。(){{ select(28) }}
- 对
- 错
- 对给定输入,闭包完成后
reach[3][6]为真。(){{ select(29) }}
- 对
- 错
- 若把
mask1中的1LL << i改成1 << i,当n=60时仍一定安全。(2分)(){{ select(30) }}
- 对
- 错
- 对给定输入,程序输出为(4分){{ select(31) }}
-
14 6 126 37780 -
14 5 63 37780 -
15 6 126 50308 -
30 6 126 50308
- 若在给定输入中再加入一条边
6 1,第一行输出会变为(4分){{ select(32) }}
14 630 630 515 6
三、完善程序(单选题,每小题 3 分,共计 30 分)
(1)(访问长度统计)
有 n 个点,每个点恰有一条出边 to[i]。从点 i 出发,不断沿出边移动,直到下一步将进入已经经过的点为止。记 ans[i] 为这个过程中经过的不同点数。求所有 ans[i] 的和与最大值。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> to(n + 1), indeg(n + 1, 0), ans(n + 1, 0);
vector<vector<int> > rev(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> to[i];
rev[to[i]].push_back(i);
____(1)____;
}
queue<int> q;
vector<int> inCycle(n + 1, 1);
for (int i = 1; i <= n; ++i)
if (indeg[i] == 0) q.push(i);
while (!q.empty()) {
int u = q.front();
q.pop();
inCycle[u] = 0;
int v = to[u];
if (____(2)____) q.push(v);
}
queue<int> bfs;
for (int i = 1; i <= n; ++i) {
if (inCycle[i] && ans[i] == 0) {
vector<int> cyc;
int u = i;
do {
cyc.push_back(u);
u = ____(3)____;
} while (u != i);
int len = (int)cyc.size();
for (int x : cyc) {
ans[x] = ____(4)____;
bfs.push(x);
}
}
}
while (!bfs.empty()) {
int u = bfs.front();
bfs.pop();
for (int v : rev[u]) {
if (ans[v]) continue;
ans[v] = ____(5)____;
bfs.push(v);
}
}
long long sum = 0;
int mx = 0;
for (int i = 1; i <= n; ++i) {
sum += ans[i];
mx = max(mx, ans[i]);
}
cout << sum << " " << mx << "\n";
return 0;
}
- ① 处应填(){{ select(33) }}
++indeg[i]--indeg[to[i]]++indeg[to[i]]indeg[i] = to[i]
- ② 处应填(){{ select(34) }}
indeg[v] == 0--indeg[v] == 0++indeg[v] == 0inCycle[v] == 0
- ③ 处应填(){{ select(35) }}
to[u]rev[u][0]to[i]u + 1
- ④ 处应填(){{ select(36) }}
len1ans[to[x]] + 1(int)rev[x].size()
- ⑤ 处应填(){{ select(37) }}
ans[u]ans[u] + 1ans[to[v]] - 1mx + 1
(2)(能量补给)
从位置 0 出发前往终点 L。初始能量为 start,每前进 1 单位距离消耗 1 点能量。途中有 n 个补给点,每个补给点有位置 pos 和补给量 gain。到达补给点后可以选择是否使用该补给,求到达终点所需使用补给的最少次数;若无法到达,输出 -1。
#include <bits/stdc++.h>
using namespace std;
struct Station {
long long pos, gain;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long L, start;
cin >> n >> L >> start;
vector<Station> a(n + 1);
for (int i = 0; i < n; ++i) {
cin >> a[i].pos >> a[i].gain;
}
a[n] = Station{L, 0};
sort(a.begin(), a.end(), ____(6)____);
priority_queue<long long> heap;
long long energy = start, last = 0;
int used = 0;
for (int i = 0; i <= n; ++i) {
long long dist = a[i].pos - last;
energy -= ____(7)____;
while (energy < 0 && !heap.empty()) {
energy += ____(8)____;
heap.pop();
++used;
}
if (energy < 0) {
cout << -1 << "\n";
return 0;
}
____(9)____;
last = a[i].pos;
}
cout << ____(10)____ << "\n";
return 0;
}
- ⑥ 处应填(){{ select(38) }}
[](const Station& x, const Station& y) { return x.pos < y.pos; }[](const Station& x, const Station& y) { return x.gain > y.gain; }greater<Station>()[](const Station& x, const Station& y) { return x.pos > y.pos; }
- ⑦ 处应填(){{ select(39) }}
a[i].poslastdista[i].gain
- ⑧ 处应填(){{ select(40) }}
heap.size()a[i].gainheap.top() - energyheap.top()
- ⑨ 处应填(){{ select(41) }}
heap.pop()heap.push(a[i].gain)energy += a[i].gain++used
- ⑩ 处应填(){{ select(42) }}
usedenergyheap.size()used + (energy < 0)