#csps1. csps1
csps1
一、单项选择题(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)
- 关于 CSP-S 第一轮常见的 GNU/Linux 编程环境与 C++ 语法,下列说法最合理的是(){{ select(1) }}
- 只要程序包含
<bits/stdc++.h>,就一定可以在任意 ISO C++14 编译器上通过 -O2会自动把所有未定义行为转化为运行时异常,便于调试- PBDS 的
tree属于 GNU 扩展,常用于实现order_of_key、find_by_order;若程序使用结构化绑定等 C++17 语法,在默认 C++14 语义下可能无法通过编译 #define int long long不会影响函数重载、数组空间和第三方库模板实例化,因此是完全安全的写法
- 阅读下面程序,输出正确的是(){{ select(2) }}
#include <bits/stdc++.h> using namespace std; void f(int& x) { cout << "L"; x += 2; } void f(const int& x) { cout << "C"; } void f(int&& x) { cout << "R"; x += 20; } template <class T> void h(T&& x) { f(forward<T>(x)); } int main() { int a = 5; auto&& r = (a); h(r); h(move(r)); h(a + 1); f(r); cout << ":" << a << "\n"; return 0; }
LRRL:27LRRL:29LCRL:29- 程序无法通过编译
- 阅读下面程序,输出为(){{ select(3) }}
#include <bits/stdc++.h> using namespace std; int main() { unsigned int y = (1u << 31) >> 30; unsigned long long x = 0; for (int i = 0; i < 64; i += 21) x |= (1ULL << i); int z = 5 & 3 == 1; cout << ((x >> 42) & 7) << " " << y << " " << z << "\n"; return 0; }
1 2 01 2 13 2 0- 程序中
1ULL << 63一定未定义,因此没有确定输出
- 阅读下面使用 PBDS 的程序,输出为(){{ select(4) }}
#include <bits/stdc++.h> #include <ext/pb_ds/assoc_container.hpp> #include <ext/pb_ds/tree_policy.hpp> using namespace std; using namespace __gnu_pbds; typedef tree<pair<int,int>, null_type, less<pair<int,int> >, rb_tree_tag, tree_order_statistics_node_update> ordered_set; int main() { ordered_set tr; tr.insert({5, 1}); tr.insert({1, 2}); tr.insert({5, 3}); tr.insert({3, 4}); tr.insert({5, 5}); cout << tr.order_of_key({5, 4}) << " "; auto it = tr.find_by_order(2); cout << it->first << "," << it->second << " "; tr.erase({5, 3}); cout << tr.order_of_key({5, 100}) << "\n"; return 0; }
3 5,3 34 5,1 44 5,3 45 3,4 4
- 关于 C++ STL 容器的迭代器和引用失效规则,下列说法正确的是(){{ select(5) }}
- 对
vector调用erase(pos)只会使指向被删元素的迭代器失效,之后元素的迭代器仍然有效 - 对
deque在中间位置插入元素时,原有迭代器一定全部有效 - 对
list进行一次合法的splice调用移动节点时,指向被移动元素的迭代器和引用仍然有效,只是元素所属容器可能改变 - 对
unordered_map调用rehash后,原有迭代器一定仍然有效
- 某有向图缩点后得到 8 个强连通分量
A,B,C,D,E,F,G,H,边为:
若不添加其他边,则至少需要添加多少条有向边才能使原图强连通?若先额外添加一条从分量A -> C, B -> C, C -> D, D -> E, F -> D, E -> G, F -> H, H -> GG指向分量A的边,再使原图强连通又至少需要多少条边?(){{ select(6) }}
2, 12, 23, 23, 1
- 二分图左部点为
1,2,3,4,5,6,右部点为a,b,c,d,e,f,边为:
关于该图,下列说法正确的是(){{ select(7) }}1-a, 1-b, 2-a, 2-c, 3-b, 3-d, 4-c, 4-e, 5-d, 5-e, 5-f, 6-f
- 最大匹配数为 6,最小点覆盖大小为 6;若删去边
6-f,最大匹配数变为 5 - 最大匹配数为 5,最小点覆盖大小为 6;若删去边
6-f,最大匹配数不变 - 最大匹配数为 6,最小点覆盖大小为 5;若删去边
6-f,最大匹配数变为 4 - 最大匹配数为 7,最小点覆盖大小为 7;若删去边
6-f,最大匹配数变为 6
- 长度为 10 的数组初始为:
依次执行以下区间加法:2, -1, 4, 0, 3, -2, 5, 1, -3, 2
若用支持区间加、区间查询的线段树维护,则操作结束后区间[2,7] 加 3 [5,10] 加 -4 [1,4] 加 2[3,9]的元素和与全局最大值分别为(){{ select(8) }}
6, 97, 97, 89, 7
- 长度为 10 的数组初始全为 0。使用树状数组维护差分,
add_range(l,r,v)等价于add(l,v)与add(r+1,-v),其中add(i,v)依次访问i, i+lowbit(i), ...,直到下标大于 10。执行:
则add_range(3, 8, 2) add_range(1, 4, 1) add_range(6, 10, -3)query(4) + query(7)的值,以及 6 次内部add累计访问的树状数组下标次数分别为(){{ select(9) }}
3, 122, 122, 145, 14
- 对下列有向带权图从 1 号点运行标准堆优化 Dijkstra,不在第一次弹出终点时提前结束,而是直到堆空为止。若每次弹出
(d,u)时发现d != dist[u]就记作一个过期状态,则dist[5]与过期状态个数分别为(){{ select(10) }}1 -> 2 (8), 1 -> 3 (3), 1 -> 5 (20) 3 -> 2 (2), 3 -> 4 (7) 2 -> 4 (2), 2 -> 5 (10) 4 -> 5 (1)
8, 07, 38, 38, 4
- 长度为 12 的 01 串中,恰有 5 个字符为
1,且不含连续子串010的串共有()个{{ select(11) }}
- 120
- 126
- 132
- 140
- 某随机过程从状态 0 开始。每一步独立地以概率
1/2使状态加 1,以概率1/2使状态变回 0;当状态第一次达到 3 时停止。停止前的期望步数为(){{ select(12) }}
- 14
- 12
- 10
- 8
- 有一个
3行4列的棋盘。每一行可以选择若干个格子,但同一行中相邻两列不能同时被选;同一列最多只能在一行中被选。问四列都恰好被选中一次的方案数,以及所有合法选择方案数分别为(){{ select(13) }}
24, 12820, 14224, 14232, 156
- 一棵树有 9 个点,边为:
点权依次为:1-2, 1-3, 1-4, 2-5, 2-6, 3-7, 4-8, 4-9
在相邻点不能同时选择的限制下,最大点权和是多少?若强制 1 号点不选,最大点权和又是多少?(){{ select(14) }}w1=6, w2=10, w3=7, w4=8, w5=5, w6=9, w7=4, w8=11, w9=3
38, 3537, 3538, 3436, 35
- 设组合数
C(n,k)=n!/(k!(n-k)!)。根据 Lucas 定理,C(2026,1024) mod 13的值为(){{ select(15) }}
012611
二、阅读程序(程序输入不超过数组或字符串定义的范围;判断题正确填对,错误填错;除特殊说明外,判断题1.5分,选择题3分,标注4分题为4分,共计40分)
(1)
#include <bits/stdc++.h>
using namespace std;
const int HM = 1009;
const int BASE = 17;
int main() {
int n, L, R;
cin >> n >> L >> R;
vector<int> a(n + 1);
vector<long long> s(n + 1);
vector<int> pw(n + 1, 1), hs(n + 1, 0);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
s[i] = s[i - 1] + a[i];
int x = ((a[i] % 10) + 10) % 10;
hs[i] = (hs[i - 1] * BASE + x) % HM;
pw[i] = 1LL * pw[i - 1] * BASE % HM;
}
deque<int> q;
int ptr = 0;
long long best = -(1LL << 60);
int ways = 0, bl = 0, br = 0, code = 0;
for (int r = 1; r <= n; ++r) {
while (ptr <= r - L) {
while (!q.empty() && s[q.back()] >= s[ptr]) q.pop_back();
q.push_back(ptr++);
}
while (!q.empty() && q.front() < r - R) q.pop_front();
if (q.empty()) continue;
int p = q.front();
long long cur = s[r] - s[p];
int sub = (hs[r] - 1LL * hs[p] * pw[r - p]) % HM;
if (sub < 0) sub += HM;
if (cur > best) {
best = cur;
ways = 1;
bl = p + 1;
br = r;
code = sub;
} else if (cur == best) {
++ways;
code += sub;
if (code >= HM) code -= HM;
}
}
cout << best << " " << ways << "\n";
cout << bl << " " << br << " " << code << "\n";
return 0;
}
给定输入:
9 2 4
3 -2 4 -1 -3 5 -2 2 1
- 队列
q中保存的是候选前缀下标;在任意时刻,队列中的下标递增,且对应前缀和严格递增。(){{ select(16) }}
- 对
- 错
- 对给定输入,当循环处理到
r=6并完成两次while后,队首下标为 2,此时cur=5。(){{ select(17) }}
- 对
- 错
- 若删除
if (sub < 0) sub += HM;,由于 C++ 的%会自动返回非负余数,所有sub仍一定在[0, HM)内。(2分)(){{ select(18) }}
- 对
- 错
- 对给定输入,程序输出为(){{ select(19) }}
-
6 1 6 9 678 -
6 3 1 3 747 -
5 3 1 3 179 -
7 1 6 9 678
- 若仅将维护队尾的条件
s[q.back()] >= s[ptr]改为s[q.back()] > s[ptr],下列说法最合理的是(){{ select(20) }}
- 最大区间和一定变大,因为保留了更多候选
- 最大区间和不变,但相同前缀和时代表区间和哈希可能变化
- 程序时间复杂度会从线性变成平方
- 改动后队列下标不再递增
- 对给定输入,所有长度在
[L,R]内且区间和等于 5 的区间共有()个{{ select(21) }}
- 1
- 2
- 3
- 4
(2)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 105;
vector<int> g[MAXN], dag[MAXN], ug[MAXN];
int dfn[MAXN], low[MAXN], bel[MAXN], sz[MAXN], indeg[MAXN];
bool inst[MAXN];
vector<int> st;
int timer = 0, scc = 0;
void tarjan(int u) {
dfn[u] = low[u] = ++timer;
st.push_back(u);
inst[u] = true;
for (int v : g[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (inst[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
++scc;
while (true) {
int x = st.back();
st.pop_back();
inst[x] = false;
bel[x] = scc;
++sz[scc];
if (x == u) break;
}
}
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 0; i < m; ++i) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i]) tarjan(i);
set<pair<int,int> > seen;
for (int u = 1; u <= n; ++u) {
for (int v : g[u]) {
int a = bel[u], b = bel[v];
if (a == b) continue;
if (seen.insert({a, b}).second) {
dag[a].push_back(b);
ug[a].push_back(b);
ug[b].push_back(a);
++indeg[b];
}
}
}
queue<int> q;
vector<int> topo;
vector<int> dp(scc + 1);
for (int i = 1; i <= scc; ++i) {
dp[i] = sz[i];
if (indeg[i] == 0) q.push(i);
}
while (!q.empty()) {
int u = q.front();
q.pop();
topo.push_back(u);
for (int v : dag[u]) {
dp[v] = max(dp[v], dp[u] + sz[v]);
if (--indeg[v] == 0) q.push(v);
}
}
bool bip = true;
vector<int> col(scc + 1, -1);
for (int i = 1; i <= scc; ++i) if (col[i] == -1) {
col[i] = 0;
queue<int> qq;
qq.push(i);
while (!qq.empty()) {
int u = qq.front();
qq.pop();
for (int v : ug[u]) {
if (col[v] == -1) {
col[v] = col[u] ^ 1;
qq.push(v);
} else if (col[v] == col[u]) {
bip = false;
}
}
}
}
int edges = (int)seen.size();
int longest = 0;
for (int x : dp) longest = max(longest, x);
int code = 0;
for (int u : topo) code = (code * 911 + u * 17 + sz[u]) % 1000003;
cout << scc << " " << edges << " " << topo.size() << "\n";
cout << longest << " " << (bip ? "BIP" : "ODD") << " " << code << "\n";
return 0;
}
给定输入:
9 13
1 2
2 3
3 1
3 4
4 5
5 4
5 6
6 9
2 7
7 8
8 7
8 6
4 7
- 对给定输入,缩点编号从 1 到 5 对应的原点集合依次为
{9}、{6}、{7,8}、{4,5}、{1,2,3}。(){{ select(22) }}
- 对
- 错
seen的作用是去除压缩图中的重边,否则同一条压缩边可能使入度被重复累加。(){{ select(23) }}
- 对
- 错
- 若把
else if (inst[v]) low[u] = min(low[u], dfn[v]);改成对所有已经访问过的v都执行low[u] = min(low[u], dfn[v]);,Tarjan 缩点结果仍一定正确。(2分)(){{ select(24) }}
- 对
- 错
- 对给定输入,程序输出为(){{ select(25) }}
-
5 6 5 9 ODD 315756 -
5 6 4 9 BIP 315756 -
4 6 4 8 ODD 68893 -
5 5 5 7 BIP 315756
- 若仅删去输入中的最后一条边
4 7,程序输出变为(){{ select(26) }}
-
5 6 5 7 ODD 315756 -
5 5 5 7 BIP 315756 -
4 5 4 9 BIP 91386 -
5 5 4 7 ODD 315756
- 对给定输入,Kahn 拓扑过程中弹出的压缩点编号序列为(){{ select(27) }}
5,4,3,2,11,2,3,4,55,3,4,2,14,5,3,2,1
(3)
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 998244353;
long long mod_pow(long long a, long long b) {
long long r = 1;
while (b) {
if (b & 1) r = r * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return r;
}
int main() {
int n, m, T;
cin >> n >> m >> T;
vector<int> ban(n + 1, 0);
for (int i = 1; i <= n; ++i) {
string s;
cin >> s;
for (int j = 0; j < m; ++j)
if (s[j] == '#') ban[i] |= (1 << j);
}
vector<int> states, bits;
for (int mask = 0; mask < (1 << m); ++mask) {
if ((mask & (mask << 1)) == 0) {
states.push_back(mask);
bits.push_back(__builtin_popcount((unsigned)mask));
}
}
int S = states.size();
int maxK = n * m;
vector<vector<long long> > dp(S, vector<long long>(maxK + 1, 0));
int zero = find(states.begin(), states.end(), 0) - states.begin();
dp[zero][0] = 1;
for (int row = 1; row <= n; ++row) {
vector<vector<long long> > ndp(S, vector<long long>(maxK + 1, 0));
for (int id = 0; id < S; ++id) {
int mask = states[id];
if (mask & ban[row]) continue;
int c = bits[id];
for (int pid = 0; pid < S; ++pid) {
int pre = states[pid];
if ((mask & pre) || (mask & (pre << 1)) || (mask & (pre >> 1))) continue;
for (int k = 0; k + c <= maxK; ++k) {
ndp[id][k + c] = (ndp[id][k + c] + dp[pid][k]) % MOD;
}
}
}
dp.swap(ndp);
}
vector<long long> ways(maxK + 1, 0);
for (int id = 0; id < S; ++id)
for (int k = 0; k <= maxK; ++k)
ways[k] = (ways[k] + dp[id][k]) % MOD;
vector<vector<long long> > C(maxK + 1, vector<long long>(maxK + 1, 0));
for (int i = 0; i <= maxK; ++i) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; ++j)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
}
long long total = 0, weighted = 0, pairs = 0;
for (int k = 0; k <= maxK; ++k) {
total = (total + ways[k]) % MOD;
weighted = (weighted + 1LL * k * ways[k]) % MOD;
if (k >= 2) pairs = (pairs + C[k][2] * ways[k]) % MOD;
}
long long expect = weighted * mod_pow(total, MOD - 2) % MOD;
cout << total << " " << ways[T] << "\n";
cout << expect << " " << pairs << "\n";
return 0;
}
给定输入:
3 4 3
....
.#..
....
- 当
m=4时,states中共有 8 个状态。(){{ select(28) }}
- 对
- 错
- 条件
(mask & (pre << 1))与(mask & (pre >> 1))会禁止相邻两行中斜向相邻的两个位置同时被选。(){{ select(29) }}
- 对
- 错
- 程序第二行输出的第一个数是普通十进制小数形式的数学期望。(2分)(){{ select(30) }}
- 对
- 错
- 对给定输入,程序输出为(){{ select(31) }}
-
88 33 294935834 187 -
88 34 107/44 187 -
93 34 294935834 214 -
82 30 214 187
- 若把给定输入第二行棋盘也改为
....,即没有障碍,且仍令T=3,则total与ways[T]分别为(4分){{ select(32) }}
88, 3393, 3393, 3496, 37
三、完善程序(单选题,每小题 3 分,共计 30 分)
(1)(区间唯一数统计)
给定长度为 n 的数组和 q 次询问。每次询问给出区间 [l,r],要求输出该区间中"恰好出现一次"的不同数的个数。下面程序将询问按右端点离线排序,并用树状数组维护每个可能左端点的贡献。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
struct Query {
int l, r, id;
};
int n, q;
int a[MAXN], last[MAXN], pre[MAXN], bit[MAXN], ans[MAXN];
vector<int> disc;
vector<Query> query;
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
for (int i = x; i <= n; i += lowbit(i)) {
bit[i] += v;
}
}
void range_add(int l, int r, int v) {
if (____(1)____) return;
add(l, v);
____(2)____;
}
int point_sum(int x) {
int res = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
res += bit[i];
}
return res;
}
void insert_pos(int pos) {
int x = a[pos];
if (last[x]) {
____(3)____;
}
____(4)____;
pre[x] = last[x];
last[x] = pos;
}
bool cmp_query(const Query& A, const Query& B) {
return ____(5)____;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
disc.reserve(n);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
disc.push_back(a[i]);
}
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
for (int i = 1; i <= n; ++i) {
a[i] = lower_bound(disc.begin(), disc.end(), a[i]) - disc.begin() + 1;
}
query.resize(q);
for (int i = 0; i < q; ++i) {
cin >> query[i].l >> query[i].r;
query[i].id = i;
}
sort(query.begin(), query.end(), cmp_query);
int cur = 0;
for (const Query& qu : query) {
while (cur < qu.r) insert_pos(++cur);
ans[qu.id] = point_sum(qu.l);
}
for (int i = 0; i < q; ++i) {
cout << ans[i] << "\n";
}
return 0;
}
- ① 处应填(){{ select(33) }}
l > rl == rl < rl + 1 > r
- ② 处应填(){{ select(34) }}
add(r, -v)add(l + 1, -v)add(r + 1, v)add(r + 1, -v)
- ③ 处应填(){{ select(35) }}
range_add(last[x] + 1, pos, -1)range_add(pre[x] + 1, last[x], -1)range_add(pre[x], last[x] - 1, -1)range_add(pre[x] + 1, pos, -1)
- ④ 处应填(){{ select(36) }}
range_add(pre[x] + 1, last[x], 1)range_add(1, pos, 1)range_add(last[x] + 1, pos, 1)range_add(pos + 1, n, 1)
- ⑤ 处应填(){{ select(37) }}
A.r < B.rA.l < B.lA.id < B.idA.r > B.r
(2)(树上限额独立集计数)
给定一棵 n 个点的树,求选出恰好 K 个点且任意相邻两点不同时被选的方案数,答案对 1000000007 取模。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 205;
const long long MOD = 1000000007LL;
int n, K, sz[MAXN];
vector<int> g[MAXN];
long long dp[MAXN][2][MAXN], tmp[2][MAXN];
void dfs(int u, int fa) {
sz[u] = 1;
____(6)____;
for (int v : g[u]) {
if (v == fa) continue;
dfs(v, u);
memset(tmp, 0, sizeof(tmp));
for (int i = 0; i <= min(sz[u], K); ++i) {
for (int j = 0; j <= min(sz[v], K - i); ++j) {
____(7)____;
____(8)____;
}
}
____(9)____;
for (int t = 0; t <= min(sz[u], K); ++t) {
dp[u][0][t] = tmp[0][t];
dp[u][1][t] = tmp[1][t];
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> K;
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
cout << ____(10)____ << "\n";
return 0;
}
- ⑥ 处应填(){{ select(38) }}
dp[u][0][0] = 1; if (K >= 1) dp[u][1][1] = 1dp[u][0][1] = 1; dp[u][1][0] = 1dp[u][0][0] = dp[u][1][0] = 1dp[u][0][1] = dp[u][1][1] = 1
- ⑦ 处应填(){{ select(39) }}
tmp[0][i + j] = (tmp[0][i + j] + dp[u][0][i] * dp[v][0][j]) % MODtmp[0][i + j] = (tmp[0][i + j] + dp[u][1][i] * (dp[v][0][j] + dp[v][1][j])) % MODtmp[0][i + j] = (tmp[0][i + j] + dp[u][0][i] * dp[v][1][j]) % MODtmp[0][i + j] = (tmp[0][i + j] + dp[u][0][i] * ((dp[v][0][j] + dp[v][1][j]) % MOD)) % MOD
- ⑧ 处应填(){{ select(40) }}
tmp[1][i + j] = (tmp[1][i + j] + dp[u][1][i] * ((dp[v][0][j] + dp[v][1][j]) % MOD)) % MODtmp[1][i + j] = (tmp[1][i + j] + dp[u][1][i] * dp[v][0][j]) % MODtmp[1][i + j] = (tmp[1][i + j] + dp[u][0][i] * dp[v][0][j]) % MODtmp[1][i + j] = (tmp[1][i + j] + dp[u][1][i] * dp[v][1][j]) % MOD
- ⑨ 处应填(){{ select(41) }}
sz[v] += sz[u]sz[u] += sz[v]sz[u] = max(sz[u], sz[v])++sz[u]
- ⑩ 处应填(){{ select(42) }}
(dp[1][0][K] + dp[1][1][K]) % MODdp[1][1][K]dp[1][0][K](dp[1][0][K] * dp[1][1][K]) % MOD