吐槽
这张卷子难点很多坑点更多,总之非常恶心。。。。。。
题目解析
单选题
A. ls 列出当前工作目录的所有文件(List)
B. cd 将指定文件夹设置为当前工作目录(Change Directory)
C. cp 将文件复制到指定路径(Copy)
D. 这啥?
选A
直接加,选B
常识 ,选A
19年也考过该知识点。
堆排序可能会交换两个相等元素的位置,是不稳定排序,选C
19年11题的变式。
如果每个数都和
m
a
x
,
m
i
n
max\ ,\ min
max , min 比较,则要
4
n
−
2
4n-2
4n−2 次
不妨把
2
n
2n
2n 个数切一半,拆成
a
i
,
b
i
a_i,b_i
ai,bi 两个序列
(
a
1
,
b
1
)
,
(
a
2
,
b
2
)
,
.
.
.
,
(
a
n
,
b
n
)
(a_1,b_1),(a_2,b_2),...,(a_n,b_n)
(a1,b1),(a2,b2),...,(an,bn) 两两比较,小的跟
m
i
n
min
min 比,大的跟
m
a
x
max
max 比,则需
1
+
3
(
n
−
1
)
=
3
n
−
2
1+3(n-1)=3n-2
1+3(n−1)=3n−2 次,
(
a
1
,
b
1
)
(a_1,b_1)
(a1,b1) 只用比一次
选B
全部算出来:
0
,
1
,
4
,
9
,
5
,
3
,
3
,
5
0,1,4,9,5,3,3,5
0,1,4,9,5,3,3,5
冲突处理:
0
,
1
,
4
,
9
,
5
,
3
,
6
,
7
0,1,4,9,5,3,6,7
0,1,4,9,5,3,6,7
选C
19年第8题,几乎一毛一样
为了让每个节点都有尽可能多的边(这样节点就最少),
可以构造一个完全图再加上孤立的点,构成非连通图。
36
36
36 条边构造完全图,设
n
n
n 个节点,则:
n
(
n
−
1
)
2
=
36
⇒
n
=
9
\dfrac{n(n-1)}{2}=36\ \ \ \ \large\Rightarrow \ \ \ \normalsize n=9
2n(n−1)=36 ⇒ n=9
所以加上一个孤立点:
a
n
s
w
e
r
=
n
+
1
=
10
answer=n+1=10
answer=n+1=10
选C
二叉树的性质:高度为
n
n
n 的满二叉树,节点共
2
n
−
1
2^n-1
2n−1 个
所以求
⌈
log
2
(
2021
)
⌉
=
11
\lceil\log_2(2021)\rceil=11
⌈log2(2021)⌉=11
选B
前序:根在前,根左右
中序:根在中,左根右
后序:根在后,左右根
无论如何先左后右
所以选D
转化,求逆序对:
D A C F E B = 4 , 1 , 3 , 6 , 5 , 2 \mathrm{DACFEB}={4,1,3,6,5,2} DACFEB=4,1,3,6,5,2
选A
就是计算
5
n
−
1
m
o
d
n
5^{n-1}\ \mathrm{mod}\ n
5n−1 mod n
注意到
n
=
23
n=23
n=23 为质数
费马小定理得值为
1
1
1
选A
T
(
n
)
=
T
(
n
−
1
)
+
T
(
n
−
2
)
⇒
T
(
n
)
=
O
(
F
i
b
n
)
T(n)=T(n-1)+T(n-2) \ \ \ \large\Rightarrow\ \ \ \normalsize T(n)=O(Fib_n)
T(n)=T(n−1)+T(n−2) ⇒ T(n)=O(Fibn)
又
F
i
b
i
=
F
i
b
i
−
1
+
F
i
b
i
−
2
Fib_i=Fib_{i-1}+Fib_{i-2}
Fibi=Fibi−1+Fibi−2,可以看作是乘
2
2
2
所以
T
(
n
)
=
O
(
2
n
)
T(n)=O(2^n)
T(n)=O(2n),选C
我的方法:分讨(贼复杂)
易知最多取 4 4 4 个
- 若选 1 1 1 个,则共 8 8 8 种
- 若选 2 2 2 个,不考虑限制 C 8 2 = 28 C_8^2=28 C82=28 种,有相邻的共 7 7 7 种,减去共 21 21 21 种
- 若选 3 3 3 个,不考虑限制 C 8 3 = 56 C_8^3=56 C83=56 种,相邻的可用只有两个苹果相邻的方案,再取剩下的苹果中任意一个,共 7 ∗ 6 = 42 7*6=42 7∗6=42 种,但三个连在一起的会被算两次,所以减掉 6 6 6 ,共 56 − ( 42 − 6 ) = 20 56-(42-6)=20 56−(42−6)=20 种
- 若选 4 4 4 个,直接枚举一下是否取第一个,间隔取,然后从后往前,把已经取的苹果换成它的后一个(保证不相邻),算一下共 5 5 5 种
所以一共 8 + 21 + 20 + 5 = 54 8+21+20+5=54 8+21+20+5=54 种
大佬的方法:DP
f i f_i fi 表示从前 i i i 个苹果中取,符合条件的方案数(不取算一种,原因看下面),则:
- 不取第 i i i 个, f i = f i + f i − 1 f_i=f_i+f_{i-1} fi=fi+fi−1
- 取第 i i i 个, f i = f i + f i − 2 f_i=f_i+f_{i-2} fi=fi+fi−2,此时前 i − 2 i-2 i−2 个是可以不取的,所以↑↑↑
所以, f i = f i − 1 + f i − 2 f_i=f_{i-1}+f_{i-2} fi=fi−1+fi−2, f 0 = 1 , f 1 = 2 f_0=1,f_1=2 f0=1,f1=2
f 8 = 55 f_8=55 f8=55,减掉不取的一种,共 54 54 54 种,选C
分讨,减掉三边相等多算的,选C
模拟dijkstra可是可以,但我觉得我看几眼就能找出来 5 , 6 , 3 , 5 5,6,3,5 5,6,3,5 这条,选B
以上就是单选题的全部内容,总体难度不高,跟往年差不多,就是有些坑掉进去了
阅读程序
说下我的总体思路:
首先扫一下21,惊奇地发现——我不会
再看程序,立方运算,输入了 a , b , c a,b,c a,b,c ,猜想大概是球心坐标(谁求椭球直接立方的。。。),
d d d 大概是半径,因为直接立方;
t t t 明显是求两点(球心)距离(的平方)
然后两个判断语句很关键,看这个:
球心距大于半径和,两球的交为 0 0 0 ,再看上一句也可以验证这点(包含关系,交为小球体积)
所以这段代码求的是球的体积交,21题 选C (2.5分到手)
有了这个上面的题就很简单了:
都是整数的平方,所以没影响,对
除以
2
2
2 会取整,错
同理,函数返回值是int,错
直接带入,对
按刚刚对代码的理解,其实就是求半径为
1
1
1 的求的体积,选D
先看第一题:
有点意思,看两个函数都有类似的递归,直觉判断是对的。
可以先看第二个,
可以发现,
j
j
j 是区间中点,将整个区间分成左右两段,
w
h
,
w
m
wh,wm
wh,wm 分别求的是左右段紧贴中点的最大子段和,
r
e
t
u
r
n
return
return 处的递归,可以看出来是
max
(
左段内最大子段和,右段内最大子段和,两段中间一部分的最大和
)
\max(左段内最大子段和,右段内最大子段和,两段中间一部分的最大和)
max(左段内最大子段和,右段内最大子段和,两段中间一部分的最大和)
就是求整个区间的最大子段和的!
再去看第一个,
注意,函数和结构体中的 h , m h,m h,m 的含义是不同的!
函数中表示区间,可以看作 l , r l,r l,r,
而结构体中,由于最后输出的是 j j j ,所以可以猜想 j j j 应该是区间最大子段和,
那么合并时, h h h 就是紧靠左边的最大子段和, max ( 自己的 h , 左段整段 + 右段的 h ) \max(自己的h,左段整段+右段的h) max(自己的h,左段整段+右段的h) ,也就是下面这个式子:
其他的差不多,可以类比线段树的做法(其实差不多就是一棵线段树)
所以可以判断,22对
除非输入问题,线段树不会出现
l
>
r
l>r
l>r 这种情况,
或者想象一个二分的过程,
(
h
+
m
)
>
>
1
(h+m)>>1
(h+m)>>1,最终只会到两端点相等,不可能左端点大于右端点,
这样最多执行一次(输入问题)
判错
注意
5
5
5 是
n
n
n,最大子段和是单个元素
11
11
11 ,判错
两题时间复杂度,由于线段树最多共 2 n − 1 2n-1 2n−1 个节点,
s o l v e 1 ( 1 , n ) solve1(1,n) solve1(1,n) 相当于遍历线段树中的每一个节点,复杂度是 O ( n ) O(n) O(n) 的,选B
s
o
l
v
e
2
(
1
,
n
)
solve2(1,n)
solve2(1,n) 再遍历每一个节点的同时还遍历了线段中的元素,
由于线段树每一层都是一个完整的区间,共
O
(
log
n
)
O(\log n)
O(logn) 层
所以遍历全部数值(元素)共需
O
(
n
log
n
)
O(n\log n)
O(nlogn),选C
所以
s
o
l
v
e
2
solve2
solve2 纯粹就是来降低难度的 (但是没有它或许我就做不出来了。。。)
注意注意注意!!!!!!
10 10 10 是 n n n !!!!!!
选B
(个人认为这题全卷第二坑)
大致扫两眼可以发现是一个加密和解密的过程,所以我们只用看
e
n
c
o
d
e
encode
encode(
d
e
c
o
d
e
decode
decode 就是反过来,直接把原串加密后比较就行)
还是先看第一题吧:
答案:错
原因:空串??????
(全卷最坑)
就是加密解密,对
所以做这种题英语要好
30解码的先略过
明显
O
(
n
)
O(n)
O(n) ,选B
坑!
t a b l e [ 0 ] table[0] table[0] 被赋值 0 x f f \mathrm{0xff} 0xff( 255 255 255) ,但 c h a r char char 是 − 128 ∼ 127 -128\sim127 −128∼127, 算一下应该是 − 1 -1 −1,选D
最后看解码:
这部分,可以看成是把
s
t
r
str
str 取三个字符,将每个字符的ASCII码转换为二进制,连在一起成为一个
24
24
24 位二进制数
然后每
6
6
6 为一截断,构成
4
4
4 个
6
6
6 位二进制数,分别对应的
b
a
s
e
base
base 的值再串起来,为编码后的字符串。
这一部分是剩下的不足
3
3
3 位的字符,不足的用
8
8
8 位
0
0
0 代替,全
0
0
0 的就是
=
=
= 。
剩下的就交给计算。
当然要有脑子地 算
来看30:
用原串反推,先分段,凭直觉取最后一段算:
d
d
d
d d d 的ASCII码为: 01100011 01100011 01100011
前 6 6 6 位: 011000 011000 011000,为 16 + 8 = 24 16+8=24 16+8=24 ,编码后应该是 Y Y Y,所以错
还是先分段,最后一段为:
p
p
p,所以是两个等号,排除 A,C
将密码串也分段(注意段长是
4
4
4 ),发现仅有
M
G
N
z
\mathrm{MGNz}
MGNz 和
M
W
N
z
\mathrm{MWNz}
MWNz 不同,对于原串
1
c
s
1cs
1cs
1
1
1 的ASCII码:
00110001
00110001
00110001
c
c
c 的ASCII码:
01100010
01100010
01100010
取
1
1
1 的后两位,
c
c
c 的前
4
4
4 位,
(
010110
)
2
=
22
(010110)_2=22
(010110)2=22 对应
W
W
W ,所以选D
这次的阅读程序题有点恶心啊。。。
第三题属实坑
完善程序
这种题感觉像dp,结合代码,又因为有了除法和减法的存在,大的数可以转移到小的数,
结合题目,想了好一会感觉有点像最短路的松弛操作
可以看出
F
[
i
]
F[i]
F[i] 应该表示数字
i
i
i 需要多少个
4
4
4,
然后有一个性质,所有的
F
[
i
]
F[i]
F[i] 中最小的值是不能再被更新为更小的值的(显然)
所以可以用类似dijkstra的方法,每次找最小的且未进行更新其他点操作的
F
[
i
]
F[i]
F[i],用它来更新即可
具体看题:
初始化,
4
4
4 是需要最少幸运数字的,即
F
[
4
]
=
1
F[4]=1
F[4]=1,没法再更新,所以选D
本来想选B,但感觉
r
<
n
r<n
r<n 怪怪的,不应该是最多需要
2
∗
n
2*n
2∗n 个幸运数字吗?
看了A,就是当
F
[
n
]
F[n]
F[n] 成为未被进行更新的最小值时,即
F
[
n
]
F[n]
F[n] 不能被修改时,这时的
F
[
n
]
F[n]
F[n] 才最优
选A
跟dijkstra找最小一样,选D
应当是用不能再更新的
F
[
i
]
F[i]
F[i] 去更新新的值,这样本次更新才不会白费(不然更新完若
F
[
i
]
F[i]
F[i] 又被更新,则本次更新就作废了)
选C
所以这题的 r r r 是一点用都没有是吗???
坑啊!!!(反正我被坑了)
没见过初赛代码这么长的!!!!!(可能是我孤陋寡闻了)
首先是笛卡尔树,这种树每个节点有一个键值二元组 ( k , w ) (k,w) (k,w) ,其中 k k k 满足 BST 性质(左子树的所有 k k k < 根的 k k k < 右子树的所有 k k k), w w w 满足堆的性质。
而对于一个序列建立笛卡尔树,则可以以序列中的位置为第一关键字 k k k,数值为第二关键字 w w w ,这样可以得到一棵这样的笛卡尔树( w w w 满足小根堆性质):
(图片源自维基百科)
可以发现笛卡尔树的每一个子树都是一个完整的区间(不然不满足BST性质)
笛卡尔树可以在 O ( n ) O(n) O(n) 的复杂度内建立,本质就是维护一条右链——从根一直走右儿子,一直到没有右儿子
放图理解一下(源自OI Wiki):
可以发现,右链始终是递增的。
而做法就是从序列最左端开始,以此往后枚举,遇到一个目标数,就在右链中找第一个小于它的数,目标数作为查找到的位置的右儿子,这样仍然满足BST性质和小根堆性质,之后,应当把右链中大于目标数的数全部作为它的左子树,如上图,可利用一个栈来维护右链:(源自OI Wiki)
伪代码
新建一个大小为 n 的空栈。用 top 来标操作前的栈顶,k 来标记当前栈顶。
For i := 1 to n
k := top
While 栈非空 且 栈顶元素 > 当前元素
k--
if 栈非空
栈顶元素.右儿子 := 当前元素
if k < top
当前元素.左儿子 := 栈顶元素的下一个元素
当前元素入栈
top := k
C++代码
for (int i = 1; i <= n; i++) {
int k = top;
while (k > 0 && h[stk[k]] > h[i]) k--;
if (k) rs[stk[k]] = i; // rs代表笛卡尔树每个节点的右儿子
if (k < top) ls[i] = stk[k + 1]; // ls代表笛卡尔树每个节点的左儿子
stk[++k] = i;
top = k;
}
还可以这么写:
struct Cartesian_Tree
{
int val, fa, ls, rs;
Cartesian_Tree(int _val = 0, int _fa = 0) { val = _val; fa = _fa; ls = rs = 0; }
}tree[N];
int root;
int Cartesian_Build(int n)
{
for(register int i = 1; i <= n; i ++ )
{
int k = i - 1;
while(tree[k].val > tree[i].val) k = tree[k].fa;
tree[i].ls = tree[k].rs;
tree[k].rs = i; //将大于目标点的节点作为目标点左子树,目标点为 k 的右儿子
tree[i].fa = k;
tree[tree[i].ls].fa = i; //将目标点的父亲更新为 k ,目标点左儿子的父亲(原来是 k)更新为目标点
}
return tree[0].rs; // 0 号节点在数列最左端,且小于任意一个数,所以其右节点就是笛卡尔树的根
}
//在main()中
root = Cartesian_Build(n);
这里在序列最左端新建了一个 0 0 0 号节点,并维护了父亲的信息(本题要使用)
言归正传
先来看这题的思路:
可以看出一段区间的RMQ就是两端点对应的笛卡尔树上的节点的LCA(子树即区间最值)
求LCA可以用Tarjan
O
(
n
)
O(n)
O(n),也可以用本题的Euler序,即区间两端点对应在Euler序中的两个位置构成的区间中,最小的值(即dfn最小,深度最浅),因为设区间两端点为
l
,
r
l,r
l,r,Euler序中两端点为
d
f
n
[
l
]
,
d
f
n
[
r
]
dfn[l],dfn[r]
dfn[l],dfn[r], 则dfs从
l
l
l 到
r
r
r 的过程中,必然是先从
l
→
L
C
A
(
l
,
r
)
l→LCA(l,r)
l→LCA(l,r),再从
L
C
A
(
l
,
r
)
→
r
LCA(l,r)→r
LCA(l,r)→r,途中不会经过比
L
C
A
LCA
LCA 更浅的点,因此
d
f
n
[
l
]
dfn[l]
dfn[l] 和
d
f
n
[
r
]
dfn[r]
dfn[r] 之间的最小值即为
L
C
A
LCA
LCA。(多个dfn不影响)
这样可以转化为相邻两数之差都为
1
,
−
1
1,-1
1,−1 的RMQ,对于这个问题:
其实是一个分块,不难理解,先暴力
O
(
n
)
O(n)
O(n) 求每一块的RMQ,将其看成一个点,再将这些“浓缩”的点整体预处理一个ST表
而对于块内,利用状态压缩枚举。
b b b 是块长,本质就是考分块的知识。
这样就可以完成本题的建树部分:
本题要建大根堆性质,最后求出的值的下一个(在右链中)是作为目标点的左儿子,所以不断令左儿子等于不满足条件的节点
选A
如果栈中(右链)还有元素,则该元素肯定大于目标数,因此该元素的右儿子是目标数,选D
v
a
l
val
val 在Euler序处理完后就没用了,这里要比的是
d
f
n
dfn
dfn 的大小,可以用深度比,选A
这里大概可以猜到是用二进制表示
±
1
±1
±1 ,由于考虑的是Euler序的RMQ,与dfn即dep有关,所以选D
这里就是按位判断,S这一位是
1
1
1 就表示序列中这一位比前一位小,减1,反之亦然,选D
就是块内查询,注意
l
→
r
l→r
l→r 的方向对应的是
D
i
f
Dif
Dif 二进制从右往左的顺序,因此从中间取一段对应
[
l
,
r
]
[l,r]
[l,r],需右移
(
l
−
p
∗
b
)
(l-p*b)
(l−p∗b) 位
选C
呼~终于写完了
这次的题坑点太多还挺麻烦,没做过这么难的初赛题(雾
好了,该结束了
END.