第十二届蓝桥杯大赛软件赛国赛(Python大学A组)

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 的括号序列,要求支持两种操作:

  1. 将 [Li​,Ri​] 区间内(序列中的第 Li​ 个字符到第Ri​ 个字符)的括号全部翻转(左括号变成右括号,右括号变成左括号)。
  2. 求出以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)
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学数学的懒哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值