2022年蓝桥杯 省赛真题
Python大学A组
试题A:裁纸刀
试题B:寻找整数
试题C:质因数个数
试题D:矩形拼接
试题E:消除游戏
试题F:重新排序
试题G:全排列的价值
试题H:最长不下降子序列
试题I:最优清零方案
试题J:数的拆分
【问题描述】
小蓝有一个裁纸刀,每次可以将一张纸沿一条直线裁成两半,小蓝用一张纸打印出两行三列共6个二维码,至少使用九次裁出来,下图给出了一种裁法。
在上面的例子中,小蓝的打印机没办法打印到边缘,所以边缘至少要裁4次。另外,小蓝每次只能裁一张纸,不能重季或者拼起来裁。如果小蓝要用一张纸打印出20行22列共440个二维码,他至少需要裁多少次?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只输出这个整数,输出多余的内容将无法得分。
【解析与方法】
一道简单的数学题 先看例子,边缘必须裁四次,然后得到两行三列共六张二维码。 横线5裁一次,竖线6 7 8 9各裁一次,加上裁边缘的四次,共九次。 也就是说,横向裁剪次数为【行数-1】次。 竖向裁剪次数为【(列数-1)*行数】次。 题目共20行22列,则次数为:4 + 19 + (21*20) = 443次。可以证明这种方法是裁剪次数最少的方法。所以当行数为20行、列数为22列的440个二维码最少需要:(20-1) + (22-1)*20 + 4 = 443
【Python程序代码】
print(20-1 + (22-1)*20 + 4)
最终结果:443
【问题描述】
有一个不超过10的17次方的正整数n,知道这个数除以2至49后的余数如下表所示,求这个正整数最小是多少?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只输出这个整数,输出多余的内容将无法得分。
【解析与方法】
首先比较明显可以看出这应该属于线性同余方程组求解的问题,题目已经表明有解,所以可以直接采用中国剩余定理来求解。
首先明确所以的质数2~49的质数ai,以及对于的余数mi.
可以等价于:
存在通解:
令通解最小且符合条件:
带入第一个式子得:
即可以令m1 = a1k1+m1,a1 = k(a1a2)/d,继续迭代得到最终结果。
【Python程序代码】
import math
a = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
m = [1,2,4,4,0,10,0,18,15,16,27,22,1,11,5]
a1,m1 = a[0],m[0]
def extended_gcd(a, b):
if b == 0:
return a, 1, 0
else:
gcd, x, y = extended_gcd(b, a % b)
return gcd, y, x - (a // b) * y
def mod(a,b):
return ((a%b)+b)%b
for i in range(1,len(a)):
d,k1,k2 = extended_gcd(a1,a[i])
k1 = mod(k1*(m[i]-m1)//d,int(math.fabs(a[i]/d)))
m1 = k1*a1 + m1
a1 = int(math.fabs(a1//d*a[i]))
print(m1)
最终结果:2022040920220409
【题目描述】
给定正整数n,请问有多少个质数是n的约数?
【输入格式】
输入的第一行包含一个整数n。
【输出格式】
输出一个整数,表示n的质数约数个数。
【样例输入】
396
【样例输出】
3
【测评用例与规模】
对于30%的评测用例,1
对于所有评测用例,1
【解析与方法】
根据测评用例规模可知,本体通过的话一定得是O(sqrt(n))的算法。最基本的数的分解只能通过8个点,还需要改进,可以先预处理一下2和3的因子,这样就可以直接从5开始步长设置为6,每次检验i,i+2,其中i+4不需要检测是因为初值为5,这样不管怎么样i+4都会是3的倍数,因子3已经被处理了,所以不需要。
【Python程序代码】
res = 0
n = int(input())
a = [2,3]
for i in a:
if n%i==0:
res += 1
while n%i==0:n//=i
i = 5
while i*i<=n:
a = [i,i+2]
for j in a:
if n%j==0:
res += 1
while n%j==0:
n//=i
i += 6
if n>1:res+=1
print(res)
【问题描述】
已知3个矩形的大小依次是 a1 x b1,a2 b和a3 x b3。用这3个矩形能拼出的所有多边形中边数最少可以是多少?例如用3x2的矩形(用A表示)、4x1的矩形(用B表示)和2x4的矩形(用C表示)可以拼出如下4边形。
例如用3x2的矩形(用A表示)、3x1的矩形(用B表示)和1x1的矩形(用C表示)可以拼出如下6边形。
【输入格式】
输入包含多组数据。
第一行包含一个整数T代表数据组数。
以下T行,每行包含6 个整数 a1,b1,a2,b2,a3,b3,其中 a1,b1 是第一个矩形的边长,a2,b2 是第二个矩形的边长,a3,b3 是第三个矩形的边长。
【输出格式】
对于每组数据,输出一个整数代表答案。
【样例输入】
2
2 3 4 1 2 4
1 2 3 4 5 6
【样例输出】
4
6
【测评用例与规模】
对于10%的评测用例,1 【解析与方法】 简单观察样例思考可以发现最多8条边,如果存在两个矩形其中有一条边是相等的则为6条,如果存在三个矩形其中有一条边是相等的则是4条。如果存在两个矩形某两条边之和为第三个矩形某条边的长度则是6条,如果这两个矩形的另外一条边是相等的则是4条。根据以上结论直接暴力组合就可以。 【Python程序代码】 【题目描述】 在一个字符串S中,如果Si=Si-1且Si!=Si+1则称Si和Si+1为边缘字符。如果Si!=Si-1且Si=Si+1则Si-1和Si也称为边缘字符。其它的字符都不是边缘字符。 【输入格式】 输入一行包含一个字符串 S。 【输出格式】 输出一行包含一个字符串表示答案,如果结果为空则输出 EMPTY。 【样例输入1】 【样例输出1】 【样例输入2】 【样例输出2】 【测评用例与规模】 对于25%的评测用例,S<=其中S表示S的长度。 【解析与方法】 看题意是比较简单的模拟,不过问题涉及到多次删除操作,对于的长度容易超时,所以可以选择采用双链表来处理删除操作,压缩时间复杂度。 【Python程序代码】 【题目描述】 给定一个数组A和一些查询 Li,Ri求数组中第Li至第Ri个元素之和。小蓝觉得这个问题很无聊,于是他想重新排列一下数组,使得最终每个查询结果的和尽可能地大。小蓝想知道相比原数组,所有查询结果的总和最多可以增加多少? 【输入格式】 输入第一行包含一个整数n。 【输出格式】 输出一行包含一个整数表示答案。 【样例输入】 【样例输出】 【样例说明】 原来的和为6+14 = 20重新排列为(1,4,5,2,3)后和为 10 +14 =24增加了4。 【测评用例规模与约定】 对于30%的评测用例,n,m<50; 【解析与方法】 题意比较简单,很容易想到使用差分,把每个数需要加的次数给求出来,查询结果最大的情况则是:次数最多的乘以数值最大的。也即是差分数组的前缀和由大到小排序,和A数组由大到小排序,对应数相乘即可。由此可以解出此题。 【Python程序代码】 【题目描述】 对于一个排列 A=(a1,a2,···,an),定义价值ci为a1至ai-1中小于ai的数的个数即 【输入格式】 输入一行包含一个整数n。 【输出格式】 输出一行包含一个整数表示答案,由于所有排列的价值之和可能很大,请 输出这个数除以998244353 的余数。 【样例输入1】 【样例输出1】 【样例输入2】 【样例输出2】 【样例说明】 【测评用例规模与约定】 对于40%的评测用例,n<20。 【解析与方法】 首先需要明确在一个全排列中正序对的数量会等于逆序对的数量。所以可以转发为求一个全排列的数对数量。1~n的全排序有n!个情况,每个情况有n*(n-1)/2的数对。所以全排列的价值可以表示为: 【Python程序与代码】 【题目描述】 给定一个长度为的整数序列:A1,A2,···,An 。现在你有一次机会将其中连续的K个数修改成任意一个相同值。请你计算如何修改可以使修改后的数列的最长不下降子序列最长,请输出这个最长的长度。 【输入格式】 输入第一行包含两个整数N和K 【输出格式】 输出一行包含一个整数表示答案。 【样例输入】 【样例输出】 【测评用例规模与约定】 对于20%的评测用例,1 【解析与方法】 首先逆序单调栈 dp 出fi,其含义为以 Ai开始的最长递增子序列,最长是多少,然后顺序单调栈dpA[1,i]中的最长递增子序列,此时栈中自底向上第j个元素表示长度为j的递增子序列,最后一个元素最小可以为多少,遍历的每一个i,我们在栈中二分查找最后一个不大于 Ai+k 的元素位置j,并尝试用 fi+k + j + k 更新答案。 【Python程序代码】 【问题描述】 给定一个长度为N的数列A1,A2,···,AN。现在小蓝想通过若干次操作将这个数列中每个数字清零。 【输入格式】 输入第一行包含两个整数N和K 【输出格式】 输出一个整数表示答案。 【样例输入】 【样例输出】 【测评用例规模与约定】 对于20%的评测用例,1 【解析与方法】 直接暴力的模拟题意+一点点优化就能通过了。直接开始遍历,每次取a[i:i+k]的最小值。如果这个最小值不为0则ans加上这个最小值。同时a[i,i+k]都减去这个最小值。再遍历a[i,i+k]找到找到最后一个为0的值的下标,令i等于这个下标。最后的ans还要加上sum(a). 【Python程序代码】 【问题描述】 给定T个正整数ai,分别问每个ai能否表示为 的形式,其中x1,x2为正整数y1,y2 为大于等于2的正整数。 【输入格式】 输入第一行包含一个整数T表示间次数。 【输出格式】 对于每次询问,如果ai能够表示为题目描述的形式则输出 yes,否则输出no。 【样例输入】 【样例输出】 【测评用例规模与约定】 对于10%的评测用例,1 【解析与思路】 【Python程序代码】t = int(input())
for _ in range(t):
res = 8
a1,b1,a2,b2,a3,b3=map(int,input().split())
a = [[a1,b1],[a2,b2],[a3,b3]]
for i in range(3):
for j in range(3):
for k in range(3):
if i==j or i==k or j==k:continue
for i1 in range(2):
for j1 in range(2):
for k1 in range(2):
if a[i][i1]==a[j][j1]+a[k][k1]:
res = min(res,6)
if a[j][1-j1]==a[k][1-k1]:
res = min(res,4)
if a[i][i1]==a[j][j1]:
res = min(res,6)
if a[i][i1]==a[j][j1]==a[k][k1]:
res = min(res,4)
print(res)
试题E:消除游戏 (15分)
对于一个给定的串 S一次操作可以一次性删除该串中的所有边缘字符(操作后可能产生新的边缘字符)。
请问经过2的64次方操作后,字符串S变成了怎样的字符串?如果结果为空则输出EMPTY。edda
EMPTY
sdfhhhhcvhhxcxnnnnshh
s
对于50%的评测用例,S<=。
对于75%的评测用例,S<=。
对于所有评测用例,S<= ,S中仅含小写字母N = 10 ** 6 + 10 # 定义一个较大的数N,作为数组的长度
pos = [] # 用于存储需要检查的字符的位置
l, r = [0] * N, [0] * N # l和r数组用于存储每个位置的左右位置信息
st = [False] * N # st数组用于标记某个位置是否已经被处理过
s = '@' + input() + '@' # 获取输入字符串,并在其首尾各添加一个'@'字符,便于处理边界情况
n = len(s) - 2 # 计算实际字符串的长度(不包括首尾的'@'字符)
# 初始化每个位置的左右位置信息
for i in range(1, n + 1):
l[i] = i - 1
r[i] = i + 1
# 定义一个函数,用于检查指定位置的字符是否需要处理
def check(i):
if s[l[i]] == '@' or s[r[i]] == '@':
return # 如果左右位置是边界('@'字符),则不需要处理
if s[l[i]] == s[i] and s[i] != s[r[i]]:
pos.append(i)
pos.append(r[i]) # 如果当前位置和其左侧位置的字符相同,但和其右侧位置的字符不同,则将当前位置和右侧位置添加到pos中
if s[l[i]] != s[i] and s[i] == s[r[i]]:
pos.append(l[i])
pos.append(i) # 如果当前位置和其左侧位置的字符不同,但和其右侧位置的字符相同,则将左侧位置和当前位置添加到pos中
# 定义一个函数,用于移除指定位置的字符
def remove(j):
r[l[j]] = r[j] # 将左侧位置的右侧位置更新为当前位置的右侧位置
l[r[j]] = l[j] # 将右侧位置的左侧位置更新为当前位置的左侧位置
st[j] = True # 标记当前位置已经被处理过
# 对每个位置调用check函数进行检查
for i in range(1, n + 1):
check(i)
# 重复进行以下操作,直到pos为空:从pos中取出一个位置,如果这个位置没有被处理过,则调用remove函数移除这个位置,并将相邻的位置添加到ne数组中。然后,将ne数组中的位置作为新的待处理位置。
while pos:
ne = []
for p in pos:
if st[p]:
continue # 如果位置已经被处理过,则跳过
remove(p) # 移除当前位置
ne.append(l[p]) # 将左侧位置添加到ne中
ne.append(r[p]) # 将右侧位置添加到ne中
pos = []
for e in ne:
if not st[e]:
check(e) # 将ne中的位置作为新的待处理位置进行检查
# 遍历每个位置,如果这个位置没有被处理过,则将对应的字符添加到结果字符串中。
ans = ""
for i in range(1, n + 1):
if not st[i]:
ans += s[i] # 将未处理位置的字符添加到结果字符串中
# 输出结果字符串或"EMPTY"
if ans:
print(ans) # 输出结果字符串
else:
print("EMPTY") # 结果字符串为空,输出"EMPTY"
试题F:重新排序 (15分)
第二行包含n个整数A1,A2,···,An,相邻两个整数之间用一个空格分隔。
第三行包含一个整数m表示查询的数目
接下来m行每行包含两个整数Li、 Ri相邻两个整数之间用一个空格分隔。5
1 2 3 4 5
2
1 3
2 5
4
对于50%的评测用例,n,m<500;
对于70%的评测用例,n,m<5000:
对于所有评测用例,1n = int(input())
a = [0] + list(map(int,input().split()))
m = int(input())
pre = [0]*(n+5)
for i in range(m):
l,r = map(int,input().split())
pre[l]+=1
pre[r+1]-=1
for i in range(1,n+1):
pre[i]+=pre[i-1]
ans1,ans2 = 0,0
for i in range(1,n+1):
if pre[i]:ans1+=a[i]*pre[i]
a.sort(reverse=True)
pre.sort(reverse=True)
now = 0
for i in pre:
if i!=0:
ans2 += a[now]*i
now += 1
else:break
print(ans2-ans1)
试题G:全排列的价值 (20分)
定义A 的价值为
给定n求1至n的全排列中所有排列的价值之和。3
9
2022
593300958
对于70%的评测用例,n<5000。
对于所有评测用例,2import os
import sys
n = int(input())
p = 998244353
res = ((n-1)*n/4)%p
for i in range(1,n+1):
res = (res*i)%p
print(int(res))
试题H:最长不下降子序列 (20分)
最长不下降子序列是指序列中的一个子序列,子序列中的每个数不小于在它之前的数。
第二行包含N个整数A1, A2,···,An。5 1
1 4 2 8 5
4
import bisect
top, s, f = 0, [0] * 100010, [0] * 100010
n, k = map(int, input().split())
a = [0] + list(map(int, input().split()))
def erfens1(x, n):
return bisect.bisect_right(s, x, 0, n)
def erfens2(x, n):
return bisect.bisect_left(s, x, 0, n)
if n == k or n == 1:
print(n)
else:
i = n
while i > k:
kk = erfens1(-a[i], top)
if kk == top:top += 1
s[kk] = -a[i]
f[i] = kk
i -= 1
top = 0
ans = 0
i = 1
while i <= n - k:
ans = max(ans, f[i + k] + erfens2(a[i + k], top))
kk = erfens1(a[i], top)
if kk == top:
top += 1
s[kk] = a[i]
i += 1
print(max(top, ans + 1) + k)
试题I:最优清零方案 (25分)
每次操作小蓝可以选择以下两种之一
1、选择一个大于0的整数将它减去1;
2、选择连续K个大于0的整数将它们各减去1。
小蓝最少经过几次操作可以将整个数列清零?
第二行包含N个整数A1,A2,···,AN。4 2
1 2 3 4
6
import sys; readline = sys.stdin.readline
read = lambda: [int(x) for x in readline().split()]
n,k = read()
a = [0] + read()
i = 1
ans = 0
while i<=n:
if i+k-1<=n:
minv = min(a[i:i+k])
if minv!=0:
ans += minv
for t in range(i,i+k):a[t]-=minv
for t in range(i,i+k):
if a[t]==0:i=t
i += 1
ans += sum(a)
print(ans)
试题J:数的拆分 (25分)
接下来T行每行包含一个正整数 ai。7
2
6
12
4
8
24
72
no
no
no
yes
yes
no
yes
对于30%的评测用例,1T<300,a<
对于60%的评测用例,1
对于所有评测用例,1not_prime = [0] * 4010
prime = []
#预处理4000以内的素数
for i in range(2, 4001):
if not_prime[i] == 0:
prime.append(i)
for j in range(2 * i, 4001, i):
not_prime[j] = 1
#判断平方数
def square_number(x):
y = int(x ** 0.5)
return y * y == x or (y + 1) * (y + 1) == x
#判断立方数
def cubic_number(x):
y = int(x ** (1 / 3))
return y ** 3 == x or (y + 1) ** 3 == x
T = int(input())
for i in range(T):
a = int(input())
#先特判平方数、立方数
if square_number(a) or cubic_number(a):
print("yes")
continue
flag = True
#枚举4000以内素因子
for i in prime:
if a % i == 0:
mi = 0
while a % i == 0:
a //= i
mi += 1
#幂次必须大于1
if mi == 1:
flag = False
break
if flag and (square_number(a) or cubic_number(a)):
print("yes")
else:
print("no")