huangx607087 的蓝桥杯PB组EXP
0.简介
大一由于被六级耽误了,没参加蓝桥杯,感觉挺遗憾的(好在最后自己六级 587 587 587)。有个舍友拿了Java B组的国奖,再加上几个以前高中一起高过OI的学弟也开始搞蓝桥杯,那自己也就直接报名参赛了。
自己在高中有NOIP全国二等奖的基础上参赛,考虑到C++ B组太卷,加上上大学以来于是报了Python B,最后估分75-93左右,在线估分是81分。
注:考试时我没有保存代码,基本是写完一题丢一题(懒得保存)。下面的代码都是自己考完重新复现的时候写的,可能与考试时写的有一定误差。由于感觉加图片太麻烦,自己就直接打字了,可能有打错字的情况请包涵 ~。
4月28日,成绩出来了,江苏省省一。
1.排列字母 [5%]
Problem
小蓝要把一共字符串中的字母按其在字母表中的顺序排列:例如 LANQIAO
排列后为 AAILNOQ
。
又如: GOODGOODSTUDYDAYDAYUP
排列后是 AADDDDDGGOOOOPSTUUYYY
求:WHERETHEREISAWILLTHEREISAWAY
排列后的结果
My Solution
直接写个代码,sort一遍过(
s="WHERETHEREISAWILLTHEREISAWAY"
t=""
for i in sorted(list(s)):
t+=i
print(t)
#AAAEEEEEEHHHIIILLRRRSSTTWWWY
2.寻找整数 [5%]
Problem
有一个不超过 1 0 17 10^{17} 1017 的整数 n n n ,已知这个数除以 2 2 2 到 49 49 49 的余数如下,求这个整数的最小值
My Solution
对于一个信安数基奈梯奈的学生来说,这个题目简直是 soeasy:直接就是中国剩余定理的应用嘛。但由于这边模数不互素,因此应该使用扩展中国剩余定理做。但一个填空题实际上并没这个必要写exCRT。
很显然,这边exCRT一定是有解的~~(这不废话嘛,无解还让你找整数?)~~。因此我们只需要让模数互素就行。在这边选择所有的素数和尽可能大的素数幂作为CRT模数就可以了。因此这里我选择所有的素数: 11 , 13 , 17 , 19 , . . . , 41 , 43 , 47 11,13,17,19,...,41,43,47 11,13,17,19,...,41,43,47 和四个最大的素数幂: 2 5 = 32 , 3 3 = 27 , 5 2 = 25 , 7 2 = 49 2^5=32,3^3=27,5^2=25,7^2=49 25=32,33=27,52=25,72=49 。为什么在保证互素的情况下,选择素数幂而不是本来的素数呢?一个是因为素数幂更大,可以扩大模数,使得答案更准确,还有一个原因是因为 x ≡ 46 ( m o d 49 ) x\equiv 46 \pmod{49} x≡46(mod49) 的条件比 x ≡ 4 ( m o d 7 ) x\equiv 4 \pmod 7 x≡4(mod7) 的条件更强,其他也同理。
自己实际上菜得连乘法逆元都不会写了,反正模数小,可以直接暴力枚举逆元(
def inverse(x,y): #求逆元,不会exgcd怎么写了,直接暴力算了
for i in range(1,y):
if(x*i%y==1):
return i
p=[11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]+[32,27,25,49]
a=[0, 10, 0, 18, 15, 16, 27, 22, 1, 11, 5]+[25,20,9,46]
M=[]
M1=[]
m=1
for i in range(len(p)):
m*=p[i]
for i in range(len(p)):
M.append(m//p[i])
for i in range(len(p)):
M1.append(inverse(M[i],p[i]))
ans=0
for i in range(len(p)):
ans+=a[i]*M[i]*M1[i]
ans%=m
print(ans)
#2022040920220409
好家伙,求出这个答案出来,直接不用检查了,肯定是对的(
3.纸张尺寸 [10%]
Problem
ISO国际标准中,A0纸的大小是 1189×841 \text{1189×841} 1189×841 ,将A0纸沿长边对折后是A1纸。大小是 841 × 594 841×594 841×594 。在对折的过程中长度直接向下取整。将 A1 纸对折后是 A2纸,以此类推。
输入纸张名称,输出纸张大小。输入内容为 A0
到 A9
之一。输出两行,每行一个整数,依次代表纸张的长和宽。
【样例输入1】
A0
【样例输出1】
1189
841
【样例输入2】
A1
【样例输出2】
841
594
My Solution
反正是两个字符,第一个字符 A
是固定的,那么数字就是第二个字符。
直接这么做就行,测了测,A4纸是 297 × 210 297×210 297×210 ,应该是对的。
s=int(input()[1:])
a,b=1189,841
while(s):
s-=1
a,b=b,a//2
print(a)
print(b)
"""
A4
297
210
"""
4.数位排序 [10%]
Problem
给出 1 1 1 到 n n n ,按以下方式排序,输出排名为 m ( ≤ n ) m(≤n) m(≤n) 的数字:
如果 a 的数位和比 b 小,那么 a 排在 b 的前面
如果 a 的数位和与 b 相等,且 a 的值比 b 小,那么 a 排在 b 的前面
例如: 2022 2022 2022 排在 409 409 409 前面,因为 2022 2022 2022 的数位和是 2 + 0 + 2 + 2 = 6 2+0+2+2=6 2+0+2+2=6 ,而 409 409 409 的数位和是 13 13 13 。并且, 2022 2022 2022 排在 6 6 6 的后面。虽然 2022 2022 2022 和 6 6 6 的数位和一样,但 6 < 2022 6<2022 6<2022,所以 6 6 6 在前, 2022 2022 2022 在后。
输入为两行,第一行为 n n n ,第二行为 m m m 。
输出为一行,包含一个整数,表示答案。
【输入样例】
13
5
【输出样例】
3
1 1 1 到 13 13 13 的排序为: 1 , 10 , 2 , 11 , 3 , 12 , 4 , 13 , 5 , 6 , 7 , 8 , 9 1,10,2,11,3,12,4,13,5,6,7,8,9 1,10,2,11,3,12,4,13,5,6,7,8,9,排名为 5 5 5 的数是 13 13 13 。
30 30 30 分数据 n ≤ 300 n≤300 n≤300 , 50 50 50 分数据 n ≤ 1000 n≤1000 n≤1000 , 100 100 100 分数据 n ≤ 1 0 6 n≤10^6 n≤106。
直接sort,考场上自测了以下,貌似一百万用时为 0.6 0.6 0.6 秒。能过。
这边我给数位加个 1 0 7 10^7 107 的权重,数值用本来的权重就行。反正 n n n 不超过 1 0 6 10^6 106,因此 1 0 7 10^7 107权重够用了。( f ( 1 0 6 ) = 11000000 , f ( 2 ) = 20000002 f(10^6)=11000000,f(2)=20000002 f(106)=11000000,f(2)=20000002)。测了下, n n n 一百万的时候IDLE耗时 0.8 0.8 0.8 秒的样子,而在CMD中耗时约 0.5 0.5 0.5 秒。不出意外应该可以过。
def f(x):
a=x
b=0
while(a):
b+=a%10
a//=10
return b*10**7+x
n=int(input())
m=int(input())
arr=list(range(1,n+1))
arr.sort(key=lambda x:f(x))
print(arr[m-1])
"""
13
5
3
"""
5.蜂巢问题[15%]
Problem
给定点 ( d 1 , p 1 , q 1 ) (d_1, p_1, q_1) (d1,p1,q1) 和点 ( d 2 , p 2 , q 2 ) (d_2,p_2,q_2) (d2,p2,q2),请问他们之间最少走多少步可以到达?
【输入格式】
输入一行包含
6
6
6 个整数
d
1
,
p
1
,
q
1
,
d
2
,
p
2
,
q
2
d_1,p_1,q_1,d_2,p_2,q_2
d1,p1,q1,d2,p2,q2表示两个点的坐标,相邻两个整数之间使用一个空格分隔。
【样例输入】
0 5 3 2 3 2
【样例输出】
7
Solution
直接建立坐标求解,不过估分时估出来是 79% 的分数,但我感觉自己写的是对的。最后具体多少分我也不清楚了。因为它最大是 1 0 9 10^{9} 109的数据,但我记得好像 64 64 64 位整数达到 2 50 2^{50} 250 量级的时候才会出现误差来着。。。
DX=[-1,-0.5,0.5,1,0.5,-0.5]
DY=[0,1,1,0,-1,-1]
s=input().split()
for i in range(len(s)):
s[i]=int(s[i])
d1,p1,q1,d2,p2,q2=s
x1=DX[d1]*p1+DX[(d1+2)%6]*q1
y1=DY[d1]*p1+DY[(d1+2)%6]*q1
x2=DX[d2]*p2+DX[(d2+2)%6]*q2
y2=DY[d2]*p2+DY[(d2+2)%6]*q2
ans=(abs(x1-x2)+abs(y1-y2)/2)
ans=int(ans)
print(ans)
"""
0 5 3 2 3 2
7
"""
6.消除游戏[15%]
Problem
【样例输入 1】
edda
【样例输出 1】
EMPTY
【样例输入 2】
sdfhhhhcvhhxcxnnnnshh
【样例输出 2】
s
S S S 的长度: 25 25 25 分数据不超过 1 0 3 10^{3} 103 , 50 50 50 分数据不超过 1 0 4 10^{4} 104 , 75 75 75 分数据不超过 1 0 5 10^{5} 105 , 100 100 100 分数据不超过 1 0 6 10^{6} 106 。
Solution
这个题目我没啥思路,但我知道, 2 64 2^{64} 264 次操作实际上就是求最后的结果,因此只要一轮结束,字符串没发生变化,那就直接break输出结果。直接写了个暴力,估分时竟然还过了 75 75 75 分。(按理说我这方法应该有 O ( n 2 ) O(n^2) O(n2) 才对啊,还是因为常数小?亦或是自己把复杂度降到了 O ( n 3 O(\sqrt {n^3} O(n3 ?)
s=input()
flag=1
while flag:
flag=0
arr=[0 for i in range(len(s))]
for i in range(1,len(s)-1):
if(s[i]==s[i+1] and s[i]!=s[i-1]):
arr[i]=arr[i-1]=1
flag=1
if(s[i]==s[i-1] and s[i]!=s[i+1]):
arr[i]=arr[i+1]=1
flag=1
t=""
for i in range(len(s)):
if(not arr[i]):
t+=s[i]
if(len(s)==0):
break
s=t
if(len(s)!=0):
print(s)
else:
print('EMPTY')
7.全排列的价值[20%]
Problem
【输入样例1】
3
【输出样例1】
9
【输入样例2】
2022
【输出样例2】
593300958
对于样例1,有如下解释:
1 2 3:0+1+2=3
1 3 2:0+1+1=2
2 1 3:0+0+2=2
2 3 1:0+1+0=1
3 1 2:0+0+1=1
3 2 1:0+0+0=0
总和是9
40 40 40 分数据 n < 20 n<20 n<20 , 70 70 70 分数据 n < 5000 n<5000 n<5000 , 100 100 100 分数据 n < 1 0 6 n<10^{6} n<106
Solution
这个题目属于我的高光时刻了。考场上自己一开四没怎么注意,但我知道这个肯定不能硬求。然后我发现样例解释中第一列都是
0
0
0 ,第二列一半
0
0
0 一半
1
1
1 ,第三列三分之一
0
0
0 ,三分之一
1
1
1,三分之一
2
2
2。于是很快想到了自己奈梯奈的概率论:第一列期望
0
0
0,第二列期望
0.5
0.5
0.5,第三列期望
1
1
1,第四列期望
1.5
1.5
1.5,那么每一列期望总和就是
0
+
0.5
+
1
+
1.5
+
.
.
.
+
n
−
1
2
=
n
(
n
−
1
)
4
0+0.5+1+1.5+...+\frac{n-1} 2=\frac{n(n-1)}4
0+0.5+1+1.5+...+2n−1=4n(n−1)
但这只是期望总和,前面还要求出
n
n
n 的阶乘。因此最后的答案是:
n
!
×
n
×
(
n
−
1
)
4
\dfrac{n!×n×(n-1)}4
4n!×n×(n−1)
考场上自己不会打逆元了,想到
998244353
998244353
998244353 是素数,直接用欧拉定理:
x
p
−
2
x^{p-2}
xp−2 是
x
x
x 的逆元。因此这边
O
(
n
)
O(n)
O(n) 求阶乘,
O
(
1
)
O(1)
O(1) 求结果(实际上应该是有一个二分快速幂,复杂度
O
(
ln
p
)
O(\ln p)
O(lnp))。最后复杂度为
O
(
n
)
O(n)
O(n)。
p=998244353
def inv(x):
return pow(x,p-2,p)
n=int(input())
E=1
for i in range(1,n+1):
E*=i
E%=p
ans=E*n*(n-1)*inv(4)%p
print(ans)
"""
2022
593300958
"""
8.技能升级[20%]
Problem
【样例输入】
3 6
10 5
9 2
8 1
【样例输出】
47
40 40 40 分数据 N , M N,M N,M 不超过 1000 1000 1000, 70 70 70 分数据 N < 1 0 4 , M < 1 0 7 N<10^4,M<10^7 N<104,M<107, 100 100 100 分数据 N < 1 0 5 , M < 1 0 9 N<10^5,M<10^9 N<105,M<109。
Solution
既然他有 M M M 次升级机会,那么尽量是要把 M M M 高效地利用——很显然,这就要求我们单次升级的最小值最大——二分答案。不过这个做法好像在数据较大的时候会超时。
每次二分单次升级允许的最小值,并计算每组分别升级了多少次。如果最后升级次数超过 M M M ,那就要调大二分的中间值,否则调小二分的中间值。
这边还有个细节:比如你二分出来结果是 6 6 6 ,但加完所有 6 6 6 以上的值之后,你还有 3 3 3 次机会,但却有 4 4 4 个 5 5 5 供选择。因此这里就需要进行一次微调了。。。还有如果最后次数没用完,但所有技能都被置 0 0 0 了,也需要微调。估计微调超时了。
A,B=[],[]
n,m=input().split()
def solveab(a,b,m):
if(m<0):
m=0
return int((a+b-m)/b)
def check(x):
cheqe=0
for i in range(n):
cheqe+=solveab(A[i],B[i],x)
return cheqe<m
n,m=int(n),int(m)
for i in range(n):
a,b=input().split()
a,b=int(a),int(b)
A.append(a)
B.append(b)
L,R=0,2**33
while L<=R:
M=(L+R)//2
if(check(M)):
R=M-1
else:
L=M+1
ans=0
for i in range(n):
x=solveab(A[i],B[i],M)
ans+=(A[i]+A[i]+B[i]-B[i]*x)*x//2
m-=x
while (m>0):
K=[]
for i in range(n):
x=solveab(A[i],B[i],M)
K.append((A[i]-B[i]*x,i))
K.sort(key=lambda x:-x[1])
cnt,K=K[0],K[1:]
if(cnt[0]<=0):
break
ans+=cnt[0]
m-=1
tup=(cnt[0]-B[cnt[1]],cnt[1])
K.append(tup)
print(ans)
9.最长不下降子序列[25%]
Problem
【输入样例】
5 1
1 4 2 8 5
【输出样例】
4
Solution
考试最后30分钟写的,直接写了个暴力 O ( n 3 ) O(n^3) O(n3) ,期望得分 20 20 20 ,也就是总分 5 5 5 分
n,m=input().split()
n,m=int(n),int(m)
arr=input().split()
for i in range(n):
arr[i]=int(arr[i])
A=-1
for i in range(n-m):
#brr=arr.copy()
for j in range(i+1,n-m):
brr=arr.copy()
brr[j:j+m]=[brr[j-1]]*m
tmp=1
for k in range(1,n):
if(brr[k]>=brr[k-1]):
tmp+=1
A=max(A,tmp)
else:
tmp=1
#print(brr,i)
print(A)
"""
5 1
1 4 2 8 5
4
"""
10.最优清零方案[25%]
Problem
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ K ≤ N ≤ 10。
对于 40% 的评测用例,1 ≤ K ≤ N ≤ 100。
对于 50% 的评测用例,1 ≤ K ≤ N ≤ 1000。
对于 60% 的评测用例,1 ≤ K ≤ N ≤ 10000。
对于 70%的评测用例,1 ≤ K ≤ N ≤ 100000。
对于所有评测用例,1 ≤ K ≤ N ≤ 1000000, 0 ≤ Ai ≤ 1000000。
【样例输入】
4 2
1 2 3 4
【样例输出】
6
考场上最后十分钟写的,过了样例,应该复杂度很高,没测,代码也丢了。