2022蓝桥杯PythonB组省赛做题记录

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} x46(mod49) 的条件比 x ≡ 4 ( m o d 7 ) x\equiv 4 \pmod 7 x4(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纸,以此类推。

输入纸张名称,输出纸张大小。输入内容为 A0A9 之一。输出两行,每行一个整数,依次代表纸张的长和宽。

【样例输入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 n300 50 50 50 分数据 n ≤ 1000 n≤1000 n1000 100 100 100 分数据 n ≤ 1 0 6 n≤10^6 n106

直接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+...+2n1=4n(n1)
但这只是期望总和,前面还要求出 n n n 的阶乘。因此最后的答案是:
n ! × n × ( n − 1 ) 4 \dfrac{n!×n×(n-1)}4 4n!×n×(n1)
考场上自己不会打逆元了,想到 998244353 998244353 998244353 是素数,直接用欧拉定理: x p − 2 x^{p-2} xp2 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

考场上最后十分钟写的,过了样例,应该复杂度很高,没测,代码也丢了。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值