2022年pythonB蓝桥杯国赛

本文介绍了2022年Python蓝桥杯国赛中的几道题目,包括使用斐波那契数列找规律、素数判断的暴力法和埃筛优化、中国剩余定理的应用以及双指针解决近似GCD问题。通过代码实现和思路解析,展示了如何利用编程技巧解决数学和算法问题。
摘要由CSDN通过智能技术生成

斐波那契与7

思路

思维、规律

我只会打暴力555,这道题看不出来是找规律

只判断个位是不是7,所以每一项数都可以对10取余。先输出前200项斐波那契数,可以看出规律:每60项数里有8个数满足要求。

202202011200能整除60。

代码

打表前200项斐波那契数:

f1 = f2 = 1
q = [0, 0]
for _ in range(3,200+1):
    f = (f1+f2)%10
    if f==7: q.append(1)
    else: q.append(0)
    f1, f2 = f2, f
print(q)

输出结果为:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 0, 1, 1, 0, 0, 0]

求解代码:

ans = 202202011200 // 60 * 8
print(ans)
# ans:26960268160

小蓝做实验

思路

数论:素数

法1:暴力

如果把primes.txt中的数据复制到命令行输入,则怎么也跑不完。只有采用文件读取的方式才能在合理的时间内运行完毕。

判断质数是试除法,99.99%的数据范围是 1 0 7 − 1 0 8 10^7-10^8 107108,时间复杂度为 O ( n ) O(\sqrt{n}) O(n ),即 O ( 1 0 4 ) O(10^4) O(104),一共 2 × 1 0 6 2\times10^{6} 2×106个数,总时间复杂度为 O ( 2 × 1 0 10 ) O(2\times10^{10}) O(2×1010)

感谢这是一道填空题,暴力手段 6min 能出答案。注意不要和我一样,在循环里输出当前循环次数,就会变很慢。

法2:埃筛

由于python的数组可以开到1e8,所以可以选择埃筛或者欧拉筛法来做,比较快。

题目说99.99%的数据都<= 1e8,所以我们先把primes.txt中的数据以1e8为分界线分成2类,对于<=1e8的数据用埃筛来做,对于>1e8的数据用试除法来判断。

代码

试除法(暴力):

# 暴力
import time
def isprime(x):
    if x==1: return 0
    i = 2
    while i<=x//i:
        if x%i==0: return 0
        i+=1
    return 1

if __name__=='__main__':
    st = time.time()
    res = 0
    f = open("primes.txt")
    for line in f:
        x = int(line.strip())
        res += isprime(x)
    print(res)
    ed = time.time()
    print("运行时间 ", (ed-st)//60, " min")
    

342693
运行时间  6.0  min

埃筛法:

# 埃筛
import time
def isprime(x):
    if x==1: return 0
    i = 2
    while i<=x//i:
        if x%i==0: return 0
        i+=1
    return 1

if __name__=='__main__':
    st = time.time()
    f = open("primes.txt")
    txt = f.read().split()
    low = [int(x) for x in txt if len(x)<=8]
    up = [int(x) for x in txt if len(x)>8]
    print(len(low), len(up)) # 1999830 170
    N = int(1e8+5)
    p = [True]*N
    for i in range(2, N):
        if not p[i]: continue
        for j in range(i+i, N, i):
            p[j] = False
    res = 0
    for x in low:
        if p[x]: res += 1

    for x in up:
        res += isprime(x)

    print(res)
    ed = time.time()
    print("运行时间 ", (ed - st), " s")
342693
运行时间  54.5586462020874  s

取模

思路

数论:中国剩余定理

感觉这题思路很难想出来,拜读了其他题解。

题目问,是否存在2个不同的数x、y,使得1 <= x < y <= m,且 n m o d    x = n m o d    y n\mod x = n\mod y nmodx=nmody。考虑它的相反面,只要x、y满足1 <= x < y <=m,则 n m o d    x ≠ n m o d    y n\mod x\neq n\mod y nmodx=nmody。所以得出,只要 n m o d    k = k − 1 n\mod k = k-1 nmodk=k11 <= k <= m, 则这组n、m不满足题意,输出No

这题代码的时间复杂度我不会算(有懂的佬请在评论区指点一下我!)。

这题还有一个坑,就是必须用python的标准输入才能ac。用input()在蓝桥云课上只能过75%样例。

代码

import sys
T = int(sys.stdin.readline().strip())
for _ in range(T):
    n, m = map(int, sys.stdin.readline().strip().split())
    flag = True
    for i in range(1, m+1):
        if n%i!=i-1:
            flag = False; break
    print('No' if flag==True else 'Yes')

内存空间

思路

模拟

读完题就觉得这是一道字符串的模拟题,不需要考虑超时的问题。每次读入一行字符串后,先根据倒数第2个字符是否为],判断该字符串是不是数组形式的定义变量。

如果不是数组形式,那么根据字符串第1个字符判断是intlong或者String。前2种情况,只需要用count()计算出字符串里,的数目;String的情况,可以用"split()字符串,再统计字符串的总长度。

如果是数组形式,先统计出数组的总长度,再根据int或者long作相应乘法。

最后输出答案时,需要进行B到其他单位的转化。

代码

def cal1(s): # 列表
    tot = 0
    li = s.split('[')
    for i in range(2, len(li)):
        t = li[i].split(']')
        tot += int(t[0])
    if s[0]=='i': tot *= 4
    else: tot *= 8
    return tot

def cal2(s): # 非列表
    tot = 0
    if s[0]=='i':
        tot = 4*(s.count(',')+1)
    elif s[0]=='l':
        tot = 8*(s.count(',')+1)
    else:
        li = s.split('\"')
        for i in range(1,len(li),2):
            tot += len(li[i])
    return tot

def turn(x):
    str1 = ['B','KB','MB','GB']
    num = []
    for _ in range(4):
        num.append(x%1024)
        x //= 1024
    out = ''
    for i in range(3, -1, -1):
        if num[i]==0: continue
        out += str(num[i])+str1[i]
    print(out)
    

if __name__=='__main__':
    T = int(input())
    res = 0
    for _ in range(T):
        s = input()
        if s[-2]==']': res += cal1(s)
        else: res += cal2(s)
    turn(res)

近似GCD

思路

双指针

自己题目没看清楚,也没有仔细分析,拆解出核心问题,主要是拜读佬的解法。

题目涉及到2个概念:子数组和近似GCD。子数组必须是原数组中的连续元素。数组的近似GCD是说,修改数组的1个或0个元素后,新数组的GCD值。(GCD就是最大公约数捏~)

那么一个子数组的近似GCD为g,有2种情况:第一,是数组的每个元素都是g的倍数,即使此时g不一定是该数组的最大公约数,最多修改一个元素也可以变成GCD;第二,是数组中只有一个元素不是g的倍数,此时修改那个非g的倍数的元素,也可以变成GCD。

由于子数组必须是连续的一截,所以考虑用双指针[j,i]来维护这段区间,并且保持该区间最左侧的元素a[j]是唯一一个不是g的倍数的元素。区间[j,i]表示以a[i]结尾的满足题意的最长子数组,所以以a[i]结尾的子数组个数为j-i,区间左端点从ji-1都满足。

双指针的时间复杂度为 O ( n ) O(n) O(n),本题 n < = 1 0 5 n<=10^5 n<=105

代码

if __name__=='__main__':
    n, g = map(int, input().split())
    a = [0]+[int(x) for x in input().split()]
    last, i, ans = 0, 1, 0
    for j in range(1, n+1):
        if a[j]%g!=0:
            i = last+1
            last = j
        if j-i+1>=2:
            ans += j-i
    print(ans)

后记

先写到这里吧,国赛已经考完了。之后有时间再来填坑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值