【CSP-S2021】第一场认证个人总结

吐槽

这张卷子难点很多坑点更多,总之非常恶心。。。。。。


题目解析

单选题

在这里插入图片描述
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 4n2
不妨把 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(n1)=3n2 次, ( 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(n1)=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 2n1
所以求 ⌈ 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 5n1 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(n1)+T(n2)      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=Fibi1+Fibi2,可以看作是乘 2 2 2
所以 T ( n ) = O ( 2 n ) T(n)=O(2^n) T(n)=O(2n)选C


在这里插入图片描述
我的方法:分讨(贼复杂)

易知最多取 4 4 4

  1. 若选 1 1 1 个,则共 8 8 8
  2. 若选 2 2 2 个,不考虑限制 C 8 2 = 28 C_8^2=28 C82=28 种,有相邻的共 7 7 7 种,减去共 21 21 21
  3. 若选 3 3 3 个,不考虑限制 C 8 3 = 56 C_8^3=56 C83=56 种,相邻的可用只有两个苹果相邻的方案,再取剩下的苹果中任意一个,共 7 ∗ 6 = 42 7*6=42 76=42 种,但三个连在一起的会被算两次,所以减掉 6 6 6 ,共 56 − ( 42 − 6 ) = 20 56-(42-6)=20 56(426)=20
  4. 若选 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 个苹果中取,符合条件的方案数(不取算一种,原因看下面),则:

  1. 不取第 i i i 个, f i = f i + f i − 1 f_i=f_i+f_{i-1} fi=fi+fi1
  2. 取第 i i i 个, f i = f i + f i − 2 f_i=f_i+f_{i-2} fi=fi+fi2,此时前 i − 2 i-2 i2 个是可以不取的,所以↑↑↑

所以, f i = f i − 1 + f i − 2 f_i=f_{i-1}+f_{i-2} fi=fi1+fi2 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 2n1 个节点,

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 128127, 算一下应该是 − 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 2n 个幸运数字吗?
看了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) lLCA(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 lr 的方向对应的是 D i f Dif Dif 二进制从右往左的顺序,因此从中间取一段对应 [ l , r ] [l,r] [l,r],需右移 ( l − p ∗ b ) (l-p*b) (lpb)
选C


呼~终于写完了
这次的题坑点太多还挺麻烦,没做过这么难的初赛题(雾

好了,该结束了

END.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值