#1.0 單項選擇題
#1.1 第1題
題面
1.十進制數 \(114\) 的相反數的 \(8\) 位二進制補碼是:
A.10001110\(\qquad\)B.10001101
C.01110010\(\qquad\)D.01110011
分析與解答
\(114\) 的相反數為 \(-114\),
\(-114\) 的二進制原碼為 \(11110010\)
所以它的補碼為原碼按位取反加一
即為 \(10001110\),選 A.
#1.2 第2題
題面
2.以下哪個網站不是 Online Judge (在線程序判題系統)? Online Judge可以查看算法題目,提交自己編寫的程序,然后可以獲得評測機反饋的結果。
A.Luogu\(\qquad\)B.Gitee\(\qquad\)C.Leetcode\(\qquad\)D.Codeforces
分析與解答
不多BB,自己點進去看[\doge]
#1.3 第3題
題面
小A用字母 \(A\) 表示 \(1\),用 \(B\) 表示 \(2\),以此類推,用 \(Z\) 表示 \(26\)。對於 \(27\) 以上的數字,可以用兩位或者更長的字符串對應,例如 \(AA\) 表示 \(27\),\(AB\) 表示 \(28\),\(AZ\) 對應 \(52\),\(AAA\) 對應 \(703\)……那么 \(BYT\) 字符串對應的數字是什么?
A.2018\(\qquad\)B.2020\(\qquad\)C.2022\(\qquad\)D.2024
分析與解答
我們發現,我們可以把它看成一種特殊的27進制數,這種數沒有 '0' 這個數字,打個比方,就像十進制數 '9' 之后直接是 '11','99' 之后直接是 '111',即第二位向第三位進一時需要的數不用加上第一位的 26 ,那么我們可以發現,每 \(27\) 向第二位進一,但上一位需保留 \(1\)(所以第二位每一個一只包含26),每 \(26^2\) 向第三位進一
通過推算可以得到下表
Str | Num | Str* |
---|---|---|
A | 1 | \(1\) |
Z | 26 | \(Z\) |
AA | 27 | \(Z + 1\) |
AZ | 52 | \(Z + Z\) |
BA | 53 | \(Z + Z + 1\) |
ZZ | 702 | \(26^2 + Z\) |
AAA | 703 | \(ZZ + 1\) |
AZZ | 1378 | \(ZZ + 26^2\) |
BZZ | 2054 | \(ZZ + 2 \times 26^2\) |
BYZ | 2038 | \(ZZ + 2 \times 26^2 - Z\) |
BYT | 2022 | \(ZZ + 2 \times 26^2 - Z - 16\) |
故選 B.
#1.4 第4題
題面
分析與解答
圖片大小計算公式:
這張圖片的大小為:
故選 B.
#1.5 第5題
題面
分析與解答
最快的排序為 桶排序,時間復雜度為 \(O(n)\),故選 A.
#1.6 第6題
題面
分析與解答
樹是一個簡單無環連通圖,故①④錯
選 C.
#1.7 第7題
題面
分析與解答
每次分成兩堆,每張卷子至少查看兩次,故為 \(42 \times 2=84\) 次
故選 A.
#1.8 第8題
題面
分析與解答
- 前序遍歷(VLR)是二叉樹遍歷的一種,也叫做先根遍歷、先序遍歷、前序周游,可記做根左右。前序遍歷首先訪問根結點然后遍歷左子樹,最后遍歷右子樹。
- 中序遍歷(LDR)是二叉樹遍歷的一種,也叫做中根遍歷、中序周游。在二叉樹中,中序遍歷首先遍歷左子樹,然后訪問根結點,最后遍歷右子樹。
由以上的定義我們可以很輕松的畫出這棵樹:
我們又知道,二叉樹中,結點 \(i\) 的左兒子編號為 \(2 \times i\),右兒子編號為 \(2 \times i+1\),
故可得下圖
所以數組最大下標為 \(13\)
故選 B.
#1.9 第9題
題面
分析與解答
優先級: \(! > \&\& > ||\)
A選項:\(a\&\&b=0\),\(b\&\&c=0\),所以 \(0||0=0\)
B選項:\(a+b>c=0\),\(b=0\),所以 \(0||0=0\)
C選項:\(!c=0\),\(!a||b=0\),所以 \(0||0=0\)
D選項:\(a+b+c=2\),C艹C++中bool運算非0即
故選 D.
#1.10 第10題
題面
分析與解答
鏈表的查詢是由前向后逐個查找,那么查詢次數最少的情況就是每次要查找的數都是鏈表的頭結點,每次只訪問一個結點,共有 \(k\) 次請求,故為 \(k\) 次
故選 B.
#1.11 第11題
題面
分析與解答
- 枚舉大法
這顯然是道排列組合,不過數據較小,可以枚舉(枚舉大法好)
要注意每個班有人數限制
i | C | B | A |
---|---|---|---|
1 | 0 | 0 | 5 |
2 | 0 | 2 | 4 |
3 | 0 | 3 | 3 |
4 | 0 | 4 | 2 |
5 | 1 | 0 | 5 |
6 | 1 | 1 | 4 |
7 | 1 | 2 | 3 |
8 | 1 | 3 | 2 |
9 | 1 | 4 | 1 |
10 | 2 | 0 | 4 |
11 | 2 | 1 | 3 |
12 | 2 | 2 | 2 |
13 | 2 | 3 | 1 |
14 | 2 | 4 | 0 |
15 | 3 | 0 | 3 |
16 | 3 | 3 | 0 |
17 | 3 | 1 | 2 |
18 | 3 | 2 | 1 |
共 \(18\) 種
- 排列組合
我們可以將這個問題看成 “有 \(6\) 個相同的小球,放進 \(3\) 個不相同的的盒子中,每個盒子有限制,可以為空”,
我們先來求 “有 \(6\) 個相同的小球,放進 \(3\) 個不相同的的盒子中,可以為空” 的所有情況,即
其中 “有一個盒子中有 \(6\) 個小球,其余盒子為空” 的 \(3\) 種情況都無法滿足條件
“有一個盒子中有 \(5\) 個小球,其余兩個盒子中,有一個盒子里有 \(1\) 個球”的情況下,這 \(5\) 個球在B盒或C盒的 \(2 \times 2=4\) 種情況無法滿足條件
對於每個盒子,盒子里有 \(5\) 個球時,都有 \(2\) 種情況,為
1 5 0,0 5 1
和0 1 5,1 0 5
“有一個盒子中有 \(4\) 個小球,其余 \(2\) 個球分散在剩下兩個盒子里” 的情況下,這 \(4\) 個球在C盒的 \(C^1_3=3\) 種情況無法滿足條件
就是將剩下兩個相同球放入兩個不同盒子里,盒子可以為空的情況數,
4 0 2,4 2 0,4 1 1
那么共有 \(28 - 3 - 4 - 3 = 18\) 種情況
故選 B.
#1.12 第12題
題面
分析與解答
各排序時間復雜度(平均):
- 插入排序 \(O(n^2)\)
- 希爾排序 \(O(n^{1.3})\)
- 選擇排序 \(O(n^2)\)
- 堆排序 \(O(n\log{n})\)
- 冒泡排序 \(O(n^2)\)
- 快速排序 \(O(n\log{n})\)
- 歸並排序 \(O(n\log{n})\)
- 基數排序 \(O(d(r+n))\)(\(d\) 表示長度,\(r\) 表示關鍵字基數,\(n\) 表示關鍵字個數)
故選 B.
#1.13 第13題
題面
分析與解答
\(x \in [a,b)\) 表示 \(a \leqslant x < b\)
rand()%M
表示生成一個在區間 \([0,M)\) 內的隨機數
所以rand()%M+a
可生成一個在區間 \([a,M+a)\) 內的隨機數
那么生成一個在區間 \([a,b)\) 內的隨機數就等價於生成一個在區間 \([a,b-a+a)\) 內的隨機數
即為rand()%(b-a)+a
故選 A.
#1.14 第14題
題面
分析與解答
我們首先看森林和完全圖的定義:
森林(forest)是 \(m\)(\(m \geqslant 0\))棵互不相交的樹的集合。
完全圖是一個簡單的無向圖,其中每對不同的頂點之間都恰連有一條邊相連。
所以,只有一棵樹也可以稱作森林
那這道題就變成了 “一個7個頂點的完全圖需要至少刪掉多少條邊才能變成一棵樹?”
一個有7個頂點的完全圖共有 \(\dfrac{7 \times (7-1)}{2}=21\)條邊
一棵有7個結點的樹共有 \(6\) 條邊
所以最少刪去 \(21-6=15\) 條邊
故選 C.
#1.15 第15題
題面
分析與解答
第37屆全國青少年信息學奧林匹克競賽(CCF NOI2020)於2020年8月16-21日在長沙市一中雨花新華都學校舉行
故選 D.
#2.0 閱讀程序
#2.1 第1題
#include<iostream>
using namespace std;
#define MAXN 20
int gu[MAXN][MAXN];
int luo(int n, int m) {
if(n <= 1 || m < 2)
return 1;
if(gu[n][m] != -1)
return gu[n][m];
int ans = 0;
for(int i = 0;i < m;i += 2)
ans += luo(n - 1,i);
gu[n][m] = ans;
return ans;
}
int main(){
int n, m;
cin >> n >> m;
for(int i = 0; i < MAXN; i++)
for(int j = 0; j < MAXN; j++)
gu[i][j] = -1;
cout << luo(n, m);
return 0;
}
1.luo函數中,\(m\) 的值不可能是奇數 ( )
在主程序main()
中,傳入luo(n,m)
,這里 \(m\) 的值由鍵盤輸入,可以為奇數,故為錯
2.若將第11行的 “<” 改為 “<=”,程序的輸出結果可能會改變 ( )
改后可能會多循環一次,結果可能會改變,故為對
3.若將第8.9.13行刪除,程序的運行的結果不變 ( )
觀察程序可以發現,二維數組gu[i][j]
是用作記憶化搜索
而第8.9.13行則是進行記憶化搜索的實現,記憶化搜索僅會減少程序運行的時間,但不會改變結果,故為對
4.在添加合適的頭文件后,將第19到21行替換為memset(gu,255,sizeof(gu));
可以起到相同的作用 ( )
第19到21行是給二維數組gu[i][j]
賦初值 \(-1\)
memset()
是給每一個byte賦值,int是 \(4 byte = 32bit\),\(255\) 的二進制是 11111111
,賦值給int后,int中儲存的數為 11111111 11111111 11111111 11111111
,
但是由於 int 是有符號的,以補碼形式儲存,第一位為符號位,自然變成了 \(-1\),故為對
同樣的,當運行 memset(gu,-1,sizeof(gu))
時,直接將 \(-1\) 以補碼形式(111111111
)賦給 int ,
int中儲存的數為 11111111 11111111 11111111 11111111
,結果也是 \(-1\)
5.若輸入數據為 4 8,則輸出為( )
A.7\(\qquad\)B.8\(\qquad\)C.15\(\qquad\)D.16
沒什么好說的,要有一顆勇於模擬的心ヽ( ̄▽ ̄)و選B.
6.最壞情況下,此程序的時間復雜度是( )
A.\(O(m^2n)\qquad\)B.\(O(nm!)\qquad\)C.\(O(n^2)\qquad\)D.\(O(n^2m)\)
不會,過/kk 選 A.
#2.2 第2題
1.錯誤
調整之后,f[i][k]
和f[k][j]
可能並沒有計算或得到的不是最優解,不能保證是全局最優解,而原程序則能保證為最優解
2.錯誤
m與輸入有關,想想為什么會有快讀這個東西
3.正確
注意數據范圍,當整個圖為一條鏈或數據足夠大時,答案會超出 \(10^7\)
4.正確
兩者順序更換對答案並無影響
5.A
我們首先需要知道這個程序是在求什么,
第14到19行,求的是原圖的全源最短路
第21到40行,則是將原圖中任意兩點並為一點(程序第 26 行的F[i][j] = F[j][i] = 0
就是將兩點間的距離改為 \(0\)),再求出此時任意兩點間最短路長度之和,取最小值,數據並不大,可以手動模擬
我們先根據給出的數據畫出這個圖:
然后列出此時任意兩點間最短路長度,如下表:
i | j | f |
---|---|---|
1 | 2 | 3 |
1 | 3 | 6 |
1 | 4 | 8 |
2 | 3 | 4 |
2 | 4 | 6 |
3 | 4 | 2 |
再根據上面的分析,依次並點,可得到:
i | j | f |
---|---|---|
1 | 2 | 0 |
1 | 3 | 6 |
1 | 4 | 7 |
2 | 3 | 6 |
2 | 4 | 5 |
3 | 4 | 2 |
tot = 26
i | j | f |
---|---|---|
1 | 2 | 3 |
1 | 3 | 0 |
1 | 4 | 2 |
2 | 3 | 3 |
2 | 4 | 5 |
3 | 4 | 2 |
tot = 15
i | j | f |
---|---|---|
1 | 2 | 3 |
1 | 3 | 2 |
1 | 4 | 0 |
2 | 3 | 4 |
2 | 4 | 3 |
3 | 4 | 2 |
tot = 14
i | j | f |
---|---|---|
1 | 2 | 3 |
1 | 3 | 3 |
1 | 4 | 5 |
2 | 3 | 0 |
2 | 4 | 2 |
3 | 4 | 2 |
tot = 15
i | j | f |
---|---|---|
1 | 2 | 3 |
1 | 3 | 5 |
1 | 4 | 3 |
2 | 3 | 2 |
2 | 4 | 0 |
3 | 4 | 2 |
tot = 15
i | j | f |
---|---|---|
1 | 2 | 3 |
1 | 3 | 6 |
1 | 4 | 6 |
2 | 3 | 6 |
2 | 4 | 6 |
3 | 4 | 0 |
tot = 27
答案顯然是 \(14\)
6.A
這奇妙的時間復雜度,肯定就是Floyd
(。-`ω´-)
#2.3 第3題
#include <bits/stdc++.h>
using namespace std;
#define MOD 19260817
#define MAXN 1005
long long A[MAXN][MAXN] = {0},sum[MAXN][MAXN] = {0};
int n,m,q;
int main(){
A[1][1] = A[1][0] = 1;
for (int i = 2;i <= 1000;i ++){
A[i][0] = 1;
for (int j = 1;j <= i;j ++)
A[i][j] = (A[i - 1][j] + A[i - 1][j - 1]) % MOD;
}
for (int i = 1;i <= 1000;i ++)
for (int j = 1;j <= 1000;j ++)
sum[i][j] = (sum[i - 1][j] + sum[i][j - 1]
- sum[i - 1][j - 1] + A[i][j] + MOD) % MOD;
int q;
cin >> q;
while (q --){
int n,m;
cin >> n >> m;
cout << sum[n][m] << endl;
}
return 0;
}
1.當i<=j
時,A[i][j]
的值是0 ( )
當i==j
時,值為1,故為錯
2.當i>j
時,A[i][j]
的值相當於從個 \(i\) 不同元素中取出 \(j\) 個元素的排列數 ( )
這里實際是 \(C^j_i\),其遞推式與代碼中的遞推式符合。故為錯
拓展-組合數遞推公式
簡單證明:從 \(n\) 個不同的數中取 \(m\) 個數,第 \(n\) 個數如果取有 \(C^{m-1}_{n-1}\) 種情況,如果不取有 \(C^{m}_{n-1}\) 種情況
3.sum[i][j]
的值 \((1 <j<i \leqslant 1000)\) 不小於 sum[i-1][j-1]
的值 ( )
注意這里有模數為 \(19260817\) (話說這個模數...好評[\doge])
4.若將第12行改為 A[i][j] = (A[i - 1][j] + A[i - 1][j - 1] + MOD) % MOD;
,程序的運行的結果不變 ( )
沒有溢出問題的話,這樣寫是對的。這個寫法也常用來對負數取模。
5.A[i][j]
\((1 \leqslant j,i \leqslant 10)\) ,的所有元素中,最大值是( )
A.126\(\qquad\)B.276\(\qquad\)C.252\(\qquad\)D.210
由組合數通項公式
可以看出,我們要找最大,要使 \(n\) 盡可能大, \(m! \cdot (n-m)!\) 盡可能小,\(10\) 以內組合數最大為 \(C^5_{10} = 252\),故選C
6.若輸入的數為 1/5 3(其中 “/” 為換行符),則輸出為( )
A.10\(\qquad\)B.35\(\qquad\)C.50\(\qquad\)D.24
觀察程序,加以簡單計算,很容易發現 sum
就是楊輝三角的前綴和。
自己計算前綴和可得選C
#3.0 補全程序
#3.1 第1題
(封禁xxs)現有n個xxs(編號為1到n),每個xxs都有一個關注者,第i個xxs的關注者是ai。
現在管理員要將其中的一些xxs的賬號封禁,但需要注意的是如果封禁了第i個人,
那么為了不打草驚蛇,就不能封禁他的關注者ai。現在想知道最多可以封禁多少個xxs。
輸入第一行是一個不超過300000的整數n,第二行是n個1到n的整數表示ai。
輸出一行,一個整數表示答案。
#include <cstdio>
using namespace std;
#define MAXN 300005
int n, ans = 0, a[MAXN], in[MAXN] = {0};
bool vis[MAXN] = {0};
void dfs(int cur, int w) {
if(vis[cur])
return;
vis[cur] = true;
if(w == 1) ans++;
①
if(②)
dfs(a[cur], ③);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
in[a[i]]++;
}
for(int i = 1; i <= n; i++)
if(!in[i]) ④;
for(int i = 1; i <= n; i++)
if(⑤) dfs(i, 0);
printf("%d\n", ans);
return 0;
}
1.①處應填( )
A.a[cur]=cur;
B.in[a[cur]]=0;
C.in[a[cur]]--;
D.in[cur]--;
該點已經遍歷過了,無論是否刪除,都不會再次遍歷,所以我們可以視為把它去掉,那么我們就要將這個結點(cur
)的關注者(a[cur]
)關注的人數(in[a[cur]]
)減一
故選 C.
2.②處應填( )
A.in[a[cur]]!=0||w==1
B.in[a[cur]]==0||w==0
C.in[a[cur]]!=0||w==0
D.in[a[cur]]==0||w==1
當這個結點(cur
)的關注者(a[cur]
)關注的人數已經清零(注意這里的清零可能是被去掉但沒被刪除),那么可以傳入看看a[cur]
是否可以刪除;
若這個結點已經被刪除(\(w=1\)),那么這個結點的關注者一定不能被刪除,便可以傳入進行標記並去掉。
故選 D.
3.③處應填( )
A.0
B.1
C.w
D.1-w
這里要與上一題結合起來,有以下兩種情況:
- 這個結點已經被刪除(\(w=1\)),那么這個結點的關注者一定不能被刪除,所以我們要使傳入的 \(w\) 為 0
- 這個結點的關注者關注的人數已經清零,那么有以下兩種情況:
- 清零的所有操作中存在刪除操作,即存在 \(w=1\) 的情況,那么顯然在刪除時便會符合第一種情況,需傳入 \(w=0\);
- 清零的所有操作都是去掉而不是刪除,即每一次 \(w\) 都為 \(0\),那么顯然這個新結點可以被刪除,需傳入 \(w=1\);
綜上,我們可以看出,每次傳入時 \(w\) 的值要取反,那么符合的代碼為1-w
故選 D.
4.④處應填( )
A.dfs(i,1)
B.dfs(i,0)
C.dfs(a[i],1)
D.dfs(a[i],0)
觀察此循環中if
語句的條件,當這個結點不是任何其他結點的關注者(這個結點關注的人數為0),那么我們可以直接將它刪掉,故應傳入 \(w=1\)
故選 A.
5.⑤處應填( )
A.!in[i]
B.in[i]
C.!vis[i]
D.vis[i]
這里就是把沒有遍歷到的點遍歷完
#3.2 第2題
(燒作業)某課作業布置了N(3≤N≤100000)個題目,第i題對應的得分是ai。
作業的總得分的計算方式為去掉作業中得分最小的一個題,剩下其它所有題目得分的平均值。
但很不幸小A遇到了一場火災,前K(1≤K≤N-2)個題目被燒了,無法記錄得分。
小A想知道,K是多少時,可以得到最高的作業得分?
作業被燒了前K頁,這時的得分是從第K+1頁到最后一頁中,去除最小得分后取平均值。
輸入第一行是整數N,第二行是n個不超過10000的非負整數表示ai。
輸出一行,若干個整數表示答案。如果有多個K,請依次升序輸出。
#include <cstdio>
#include <cmath>
#define min(a,b) (a<b?a:b)
#define MAXN 100002
using namespace std;
int n, k[MAXN], cnt = 0;
int s[MAXN], minScore, sum;
double maxAverage = 0, nowAverage;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &s[i]);
minScore = s[n];
①;
for(int i = n - 1; i >= 2; i--) {
minScore = min(minScore, s[i]);
②;
nowAverage = ③;
if(nowAverage > maxAverage) {
④
maxAverage = nowAverage;
} else if(fabs(nowAverage - maxAverage) < 1e-6)
⑤;
}
for(int i = cnt; i >= 1; i--)
printf("%d\n", k[i]);
return 0;
}
1.①處應填( )
A.sum=n
B.sum=s[1]
C.sum=s[n]
D.sum=0
觀察程序,顯然是從最后一頁開始,進行累加,所以初始值為sum=s[n]
故選 C.
2.②處應填( )
A.sum=maxAverage*(n-i)
B.sum+=s[i]
C.sum+=s[n-i]
D.sum=s[i]+minScore
這里是進行累加,顯然是將 s[i]
的值加入 sum
故選 B.
3.③處應填( )
A.(double)(sum+minScore)/(n-i)
B.sum*1.0/(n-i)
C.(int)(sum-minScore)/(n-i)
D.(double)(sum-minScore)/(n-i)
讀題,要求是去掉最小值,再取平均值,nowAverag
的類型是double
故選 D.
4.④處應填( )
A.k[++cnt]=i;
B.k[cnt++]=i-1
C.cnt=1;k[cnt]=i-1;
D.cnt=0;k[cnt]=i;
當當前平均值大於以往最大平均值,我們要更新最大值,而 cnt
是計數,可能有多個相似的結果,所以我們要先將 cnt
置為 \(1\),再進行更新
故選 C.
5.⑤處應填( )
A.k[cnt++]=i;
B.k[++cnt]=i-1;
C.k[cnt++]=n-i;
D.k[cnt]=i;
當有近似值時增加可能的 \(k\) 值,但顯然 cnt
在第一次將 \(k\) 存入時,並沒有自增加,所以這里要先自加一
故選 B.
#4.0 補充說明
第11題排列組合不懂的朋友可以看一下下面這篇博客:
最后祝各位 \(CSP2020 \quad rp++\) !
更新日志及說明
更新
- 初次完成編輯 - \(2020.10.9\)
- 增添了 #2.2 閱讀程序第2題第5小題 的模擬過程
添加了 #2.3 閱讀程序第3題 - \(2020.10.10\)- 添加了 #3.0 補全程序 - \(2020.10.11\)
本文若有更改或補充會持續更新