斐波那契与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 107−108,时间复杂度为 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=k−1,1 <= 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个字符判断是int
、long
或者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
,区间左端点从j
到i-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)
后记
先写到这里吧,国赛已经考完了。之后有时间再来填坑。