2021年蓝桥杯 省赛真题
Python大学组
试题A:带宽
试题B:纯质数
试题C:完全日期
试题D:最小权值
试题E:大写
试题F:123
试题G:冰山
试题H:和与乘积
试题I:二进制问题
试题J:翻转括号序列
试题A:带宽 (5分)
【题目描述】
小蓝家的网络带宽是 200 Mbps,请问,使用小蓝家的网络理论上每秒钟最多可以从网上下载多少 MB 的内容。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只输出这个整数,输出多余的内容将无法得分。
【解析与方法】
200Mbps,也就是每秒200M个bit,1Byte=8bit,所以答案就是200/8.
【Python程序与代码
print(int(200/8))
最终结果:25
试题B:纯质数 (5分)
【题目描述】
如果一个正整数只有 11 和它本身两个约数,则称为一个质数(又称素数)。前几个质数是:2,3,5,7,11,13,17,19,23,29,31,37,⋅⋅⋅。如果一个质数的所有十进制数位都是质数,我们称它为纯质数。例如:2,3,5,7,23,37都是纯质数,而 11,13,17,19,29,31不是纯质数。当然 1,4,35也不是纯质数。
请问,在 1 到 20210605 中,有多少个纯质数?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只输出这个整数,输出多余的内容将无法得分。
【解析与方法】
线性筛筛出质数,然后对每一个质数进行数位判断是不是质数就ok.
【Python程序代码】
#基本思路:线性筛+数位判断
primes,st = [],[True]*(20210607)
def pd(x):# 2 3 5 7
while x:
if x%10 not in [2,3,5,7]:return False
x//=10
return True
res = 0
N = 20210606
for i in range(2,N):
if st[i]:
primes.append(i)
if pd(i):res+=1
for j in range(len(primes)):
k = i*primes[j]
if k>=N:break
st[k]=False
if i%primes[j]==0:break
print(res)
print(1903)
最终结果:1903
试题C:完全日期 (10分)
【题目描述】
如果一个日期中年月日的各位数字之和是完全平方数,则称为一个完全日期。例如:2021年6月5日的各位数字之和为 2+0+2+1+6+5=16,而16是一个完全平方数,它是 4 的平方。所以 2021年6月5日是一个完全日期。
例如:2021年6月23日的各位数字之和为 2+0+2+1+6+2+3=16,是一个完全平方数。 2021年6月23日也是一个完全日期。
请问,从2001年1月1日到2021年12月31日中,一共有多少个完全日期?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只输出这个整数,输出多余的内容将无法得分。
【解析与方法】
暴力枚举出每一个日期,然后判断是否符合条件。
【Python程序代码】
mon = [0,31,28,31,30,31,30,31,31,30,31,30,31]
def run(x):
if x%400==0 or (x%4==0 and x%100!=0):return True
return False
res = 0
def pd(x):# x为字符串
x = [int(i) for i in x]
sumx = sum(x)
if int(sumx**0.5)*int(sumx**0.5)==sumx:return True
return False
for year in range(2001,2022):
if run(year):mon[2]=29
else:mon[2]=28
for yue in range(1,13):
for day in range(1,mon[yue]+1):
tep = str(year)+str(yue)+str(day)
if pd(tep):res+=1
print(res)
最终结果:977
试题 D:最小权值 (10分)
【题目描述】
对于一棵有根二叉树 T,小蓝定义这棵树中结点的权值W(T) 如下:空子树的权值为0。
如果一个结点 v 有左子树 L, 右子树 R,分别有 C(L) 和 C(R) 个结点则W(v)=1+2W(L)+3W(R)+(C(L))²C(R),树的权值定义为树的根结点的权值。
小蓝想知道,对于一棵有2021个结点的二叉树,树的权值最小可能是多少?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只输出这个整数,输出多余的内容将无法得分。
【解析与方法】
这不是二叉树的问题,是一个给了状态方程的DP问题。按方程DP就行。
【Python程序代码】
f = [0]*2025
for i in range(1,2022):
f[i]=int(1e10)
for j in range(i):
f[i] = min(f[i],1+2*f[j]+3*f[i-j-1]+j*j*(i-j-1))
print(f[2021])
最终结果:2653631372
试题E:大写 (15分)
【题目描述】
给定一个只包含大写字母和小写字母的字符串,请将其中所有的小写字母转换成大写字母后将字符串输出。
【输入描述】
输入一行包含一个字符串。
【输出描述】
输出转换成大写后的字符串。
【输入样例】
LanQiao
【输出样例】
LANQIAO
【测试用例与规模】
对于所有评测用例,字符串的长度不超过 100。
【解析与方法】
语法:s.upper()
【Python程序代码】
s = input()
print(s.upper())
试题F:123 (15分)
【题目描述】
小蓝发现了一个有趣的数列,这个数列的前几项如下:1,1,2,1,2,3,1,2,3,4,⋯小蓝发现,这个数列前 1 项是整数 1,接下来 2项是整数 1 至 2,接下来 3 项是整数 1 至 3,接下来 4 项是整数 1至4,依次类推。
小蓝想知道,这个数列中,连续一段的和是多少。
【输入描述】
输入的第一行包含一个整数 T,表示询问的个数。
接下来 T 行,每行包含一组询问,其中第 i 行包含两个整数li和ri,表示询问数列中第 li 个数到第 ri 个数的和。
【输出描述】
输出 T 行,每行包含一个整数表示对应询问的答案。
【输入样例】
3
1 1
1 3
5 8
【输出样例】
1
4
8
【测试用例与规模】
对于所有评测用例,1≤T≤100000,1≤li≤ri≤10¹²。
【解析与方法】
看测试用例的规模明显在看数据形状明显的一个二分,然后就慢慢凑了,这里有了一个二次前缀和,一次前缀和表示数字到数字i含有的数字的个数,这里的i指的是第几个1,2,3...i,二维的话即是前面的i轮1,2,3...i的和,所以就很简单了,二分出第一个大于等于l,r的数,先求前缀和后记得减去多加的数就ok了,主要开的数组不能太大了会爆内存也不能太小了。
【Python程序代码】
t = int(input())
num = [[int(_), 0,0] for _ in range(1500002)]
for i in range(1, 1500001):
num[i][1] = num[i - 1][1] + num[i][0]
num[i][2] = num[i - 1][2] + num[i][1]
def erfen(x):
l, r = 0, 1500001
# 找到第一个大于x的下标
while l < r:
mid = (l + r) >> 1
if num[mid][1] >= x:
r = mid
else:
l = mid + 1
return r
def solve(l, r):
x1 = erfen(l)
x2 = erfen(r)
res = num[x2][2] - num[x1-1][2]
# 减去多加的
cnt1 = l - num[x1 - 1][1] - 1
res -= cnt1 * (1 + cnt1) // 2
cnt2 = num[x2][1] - r
res -= cnt2 * (x2 + x2 - cnt2 + 1) // 2
print(res)
for i in range(t):
l, r = map(int, input().split())
solve(l, r)
试题G:冰山 (20分)
【题目描述】
一片海域上有一些冰山,第 i 座冰山的体积为 Vi。随着气温的变化,冰山的体积可能增大或缩小。第 i 天,每座冰山的变化量都是 Xi。当Xi>0 时,所有冰山体积增加 Xi;当 Xi<0 时,所有冰山体积减少 −Xi;当 Xi=0 时,所有冰山体积不变。
如果第 i 天某座冰山的体积变化后小于等于 00,则冰山会永远消失。冰山有大小限制 k。如果第 i 天某座冰山 j 的体积变化后 Vj 大于 k,则它会分裂成一个体积为 k 的冰山和 Vj−k 座体积为1 的冰山。
第 i 天结束前(冰山增大、缩小、消失、分裂完成后),会漂来一座体积为 Yi 的冰山(Yi=0 表示没有冰山漂来)。 小蓝在连续的 m 天对这片海域进行了观察,并准确记录了冰山的变化。
小蓝想知道,每天结束时所有冰山的体积之和(包括新漂来的)是多少。 由于答案可能很大,请输出答案除以998244353的余数。
【输入描述】
输入的第一行包含三个整数 n,m,k,分别表示初始时冰山的数量、观察的天数以及冰山的大小限制。
第二行包含 n 个整数 V1,V2,⋯,Vn,表示初始时每座冰山的体积。 接下来m 行描述观察的 m 天的冰山变化。其中第 i 行包含两个整数Xi,Yi,意义如前所述。
【输出描述】
输出 m 行,每行包含一个整数,分别对应每天结束时所有冰山的体积之和除以 998244353的余数。
【输入样例】
1 3 6
1
6 1
2 2
-1 1
【输出样例】
8
16
11
【解析与方法】
实在是想不出怎么好的办法,索性就写了一个暴力,在蓝桥云上竟然过了70%的样例,基本上已经还行了。如果各位uu有什么好的方法可以戳我一下。
【Python程序代码】
import sys
from collections import *
n, m, k = map(int,sys.stdin.readline().split())
tep = list(map(int,sys.stdin.readline().split()))
dic1 = defaultdict(int)
dic2 = defaultdict(int)
p = 998244353
for i in tep:
dic1[i] += 1
for i in range(m):
x, y = map(int, sys.stdin.readline().split())
res = 0
for key in dic1.keys():
nkey = key + x
if nkey > k:
dic2[k] += dic1[key]
dic2[1] += (nkey - k) * dic1[key]
res += nkey * dic1[key]
elif 0 < nkey <= k:
dic2[nkey] += dic1[key]
res += nkey * dic1[key]
res += y
if y:dic2[y] += 1
print(res%p)
dic1 = dic2
dic2 = defaultdict(int)
试题H:和与乘积 (20分)
【题目描述】
给定一个数列A=(a1,a2,⋯,an),问有多少个区间 [L,R] 满足区间内元素的乘积等于他们的和,即 aL⋅aL+1⋯aR=aL+aL+1+⋯+aR 。
【输入描述】
输入第一行包含一个整数 n,表示数列的长度。
第二行包含 n 个整数,依次表示数列中的数 a1,a2,⋯,an。
【输出描述】
输出仅一行,包含一个整数表示满足如上条件的区间的个数。
【输入样例】
4
1 3 2 2
【输出样例】
6
【解析与方法】
可以发现在暴力的基础上容易出现关于1的多次重复处理的问题。所以可以考虑先把1处理一下,预处理出pre[i],one[i],num[i]三个数组,num[i]表示去除1后的新的数组,one[i]这表示这个新数组的下标为i时对应原数组的1的个数,pre[i]则是新数组的前缀和同时包含了原数组对应位置的1的和。初始化ans=n,一个数的乘积和它的和肯定相等,接着两层循环判断位置i,j是否符合区间和等于区间积。小tip是当前缀积大于所有的和res后推出循环,以及当one[i]+one[j+1]>d and d>0时,可以有多种组合情况。这部分可以直接看代码。
【Python程序代码】
n = int(input())
a = [0]+list(map(int,input().split()))
pre,one,num,k = [0]*(n+5),[0]*(n+5),[0]*(n+5),1
for i in range(1,n+1):
if a[i]==1:
one[k]+=1
pre[k]+=1
else:
pre[k]+=pre[k-1]+a[i]
num[k]=a[i]
k+=1
res = pre[k-1]+one[k]
ans = n
for i in range(1,k):
p = num[i]
for j in range(i+1,k):
p *= num[j]
if p>res:break
d = p - (pre[j]-pre[i-1]-one[i])
if d==0:ans+=1
elif one[i]+one[j+1]>=d and d>0:
l = min(d,one[i])
r = min(d,one[j+1])
ans += l+r-d+1
print(ans)
试题 I:二进制问题 (25分)
【题目描述】
小蓝最近在学习二进制。他想知道 1 到 N 中有多少个数满足其二进制表示中恰好有 K 个1。你能帮助他吗?
【输入描述】
输入一行包含两个整数 N 和 K。
【输出描述】
输出一个整数表示答案。
【输入样例】
7 2
【输出样例】
3
【解析与方法】
数位DP,同样也有其他方法,可以先求出n的二进制的位数的的数量,进而算出全部的这个二进制长度的数中含有1的数量为k的全部方案数量,接着可以把这些2进制数中的大于n的去掉。去掉的方法也比较简单,循环判断n的二进制的每一位,当遇到该为为0时,假设该位为1,然后从后面的二进制数位中组合选出符合要求的方案,见代码。
【Python程序代码】
def C(a,b):
res = 1
for i in range(1,b+1):
res = res*(a-i+1)//i
return res
n,k = map(int,input().split())
l = bin(n)[2:]
le = len(l)
ans = C(le,k)
cnt0,tep=0,0
for i in range(len(l)):
if l[i]=='0':
tep = i - cnt0 +1 #前i位1的数量并假设当前的0改成1
if k-tep>0:
ans -= C(le-i-1,k-tep)
else:
break
cnt0+=1
print(ans)
试题J:翻转括号序列 (25分)
【题目描述】
给定一个长度为 n 的括号序列,要求支持两种操作:
- 将 [Li,Ri] 区间内(序列中的第 Li 个字符到第Ri 个字符)的括号全部翻转(左括号变成右括号,右括号变成左括号)。
- 求出以Li 为左端点时,最长的合法括号序列对应的Ri(即找出最大的Ri 使 [Li,Ri] 是一个合法括号序列)。
【输入描述】
输入的第一行包含两个整数 n,m,分别表示括号序列长度和操作次数。第二行包含给定的括号序列,括号序列中只包含左括号和右括号。
接下来m 行,每行描述一个操作。如果该行为 “1,Li ,Ri”,表示第一种操作,区间为 [Li,Ri];如果该行为 “2 Li” 表示第二种操作,左端点为 Li。
【输出描述】
对于每个第二种操作,输出一行,表示对应的 Ri。如果不存在这样的 Ri,请输出 0。
【输入样例】
7 5
((())()
2 3
2 2
1 3 5
2 3
2 1
【输出样例】
4
7
0
0
【解析与方法】
看起来像线段树,不过还是先交了一发暴力,居然过了83.3%。在比赛中这样的结果已经是非常好的了,耗时10分组拿接近21分,给其他题目留更多的时间。虽然是暴力不过也是有一些tips的,首先就是这个sys的快读,其次括号的处理换成-1,1,方便很多。当遇到1操作时只需要遍历乘以-1,2操作也是当curr_sum<0直接退出一定是不符合要求的。
【Python程序代码】 (暴力83.3%)
import sys
n, m = map(int, sys.stdin.readline().split())
a = [0] * (n + 1)
s = input().strip()
for i in range(1, n + 1):
if s[i - 1] == '(':
a[i] = 1
else:
a[i] = -1
for _ in range(m):
op = list(map(int, sys.stdin.readline().split()))
if op[0] == 1:
l, r = op[1], op[2]
for i in range(l, r + 1):
a[i] *= -1
else:
l = op[1]
curr_sum, r = a[l], 0
for i in range(l + 1, n + 1):
if curr_sum < 0:
break
curr_sum += a[i]
if curr_sum == 0:
r = i
print(r)