Featured image of post 第十三届蓝桥杯国赛PythonA题解

第十三届蓝桥杯国赛PythonA题解

蓝桥杯题解

A 斐波那契与7

简要题意:求斐波那契数列的第1至202202011200项中,有多少项的个位是7。

解题思路

  • 直接暴力计算所有项不可行,因为数据量过大。
  • 通过打表观察发现个位数每300项会出现循环规律。
  • 预先计算循环周期内个位为7的项数(40个),然后计算完整周期数和剩余项数。

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
a = [14, 16, 17, 23, 34, 37, 43, 56, 74, 76, 77, 83, 94, 97,
     103, 116, 134, 136, 137, 143, 154, 157, 163, 176, 194, 196, 197,
     203, 214, 217, 223, 236, 254, 256, 257, 263, 274, 277, 283, 296]

ans = 202202011200 // 300 * 40
mo = 202202011200 % 300
for x in a:
    if mo >= x:
        ans += 1
    else:
        break
print(ans)

B 火柴棒数字

简要题意:用300根火柴棒拼出最大的整数。

解题思路

  • 要使数字最大,首先位数要尽可能多。
  • 统计各数字的火柴消耗,优先使用消耗少的数字(1消耗最少,但0不能作为前导)。
  • 发现可以正好用9,7,5,4,3,2,1各10个拼出最大数字。

代码实现

1
print(9999999999777777777755555555554444444444333333333322222222221111111111)

C 取模

简要题意:给定n,m,判断是否存在不同的x,y满足1≤x<y≤m且n mod x = n mod y。

解题思路

  • 使用鸽巢原理,若n mod i必须等于i-1才能避免重复。
  • 这个条件非常严格,实际只需检查前几项即可快速判断。

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
t = int(input())
for _ in range(t):
    n, m = map(int, input().split())
    f = 0
    for i in range(1, m + 1):
        if n % i != i - 1:
            print("Yes")
            f = 1
            break
    if not f:
        print("No")

D 最大公约数

简要题意:求满足gcd(x,y)=k的数对个数。

解题思路

  • 使用莫比乌斯反演来高效计算区间内的合法数对。
  • 预处理莫比乌斯函数,然后通过分块计算加速。

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import sys
import math

def preprocess_mobius(max_n):
    max_n += 1
    is_prime = [True] * max_n
    is_prime[0] = is_prime[1] = False
    mu = [1] * max_n
    for i in range(2, max_n):
        if is_prime[i]:
            for j in range(i, max_n, i):
                is_prime[j] = (j == i)
                mu[j] *= -1
            j = i * i
            for k in range(j, max_n, j):
                mu[k] = 0
    return mu

max_num = 5 * 10**4
mu = preprocess_mobius(max_num)
prefix_mu = [0] * (max_num + 1)
for i in range(1, max_num + 1):
    prefix_mu[i] = prefix_mu[i-1] + mu[i]

def compute_f(a, b):
    min_val = min(a, b)
    res = 0
    i = 1
    while i <= min_val:
        q_a = a // i
        q_b = b // i
        next_i = min(a // q_a + 1, b // q_b + 1) if (q_a !=0 and q_b !=0) else min_val + 1
        next_i = min(next_i, min_val + 1)
        res += (prefix_mu[next_i - 1] - prefix_mu[i - 1]) * q_a * q_b
        i = next_i
    return res

n = int(sys.stdin.readline())
for _ in range(n):
    a, b, c, d, k = map(int, sys.stdin.readline().split())
    a = (a - 1) // k
    b = b // k
    c = (c - 1) // k
    d = d // k
    total = compute_f(b, d) - compute_f(a, d) - compute_f(b, c) + compute_f(a, c)
    print(total)

E 数组个数

简要题意:构造满足特定条件的数组方案数。

解题思路

  • 枚举前两个数,然后动态规划计算后续数的可能情况。
  • 检查每个位置的约束条件并累加合法方案。

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
mod = 1000000007
n = int(input())
a = list(map(int, input().split()))

def check(x, y):
    res = 0
    dp = {}
    dp[(x, y)] = 1
    for h in range(2, n):
        new_dp = {}
        for i, j in dp:
            for k in range(11):
                if max(i, j, k) == a[h-1]:
                    new_dp[(j, k)] = new_dp.get((j, k), 0) + dp[(i, j)]
                    new_dp[(j, k)] %= mod
        dp = new_dp
    for i, j in dp:
        if max(i, j, x) == a[-1] and max(x, y, j) == a[0]:
            res += dp[(i, j)]
            res %= mod
    return res

ans = 0
for x in range(11):
    for y in range(11):
        ans = (ans + check(x, y)) % mod
print(ans)

F 六六大顺

简要题意:计算特定数列的和。

解题思路

  • 发现数列有数学规律,可通过公式直接计算。
  • 使用快速幂优化大数运算。

代码实现

1
2
3
n = int(input())
ans = 4 * (pow(100, n + 1) - 22 * pow(10, n + 1) + 120 + 99 * n)
print(ans // 891)

G 环境治理

简要题意:通过调整道路灰尘使总灰尘达标的最小天数。

解题思路

  • 二分答案结合Floyd算法计算最短路径。
  • 检查中间结果是否满足约束条件。

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from copy import deepcopy
n, q = map(int, input().split())
d = [[0] * (n + 2) for _ in range(n + 2)]
low = [[0] * (n + 2) for _ in range(n + 2)]

for i in range(1, n + 1):
    td = list(map(int, input().split()))
    for j in range(1, n + 1):
        d[i][j] = td[j - 1]

for i in range(1, n + 1):
    td = list(map(int, input().split()))
    for j in range(1, n + 1):
        low[i][j] = td[j - 1]

def floyd(dist):
    for k in range(1, n + 1):
        for i in range(1, n + 1):
            for j in range(1, n + 1):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    ans = 0
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            ans += dist[i][j]
    return ans

def check(x):
    cd = deepcopy(d)
    for i in range(1, n + 1):
        tmp = x // n + (1 if x % n >= i else 0)
        for j in range(1, n + 1):
            cd[i][j] = max(cd[i][j] - tmp, low[i][j])
            cd[j][i] = max(cd[j][i] - tmp, low[j][i])
    res = floyd(cd)
    return res <= q

l, r = 0, (1 << 32) + 5
while l + 1 < r:
    mid = l + r >> 1
    if check(mid):
        r = mid
    else:
        l = mid
print(r if r != (1 << 32) + 5 else -1)

H 宝石收集

题目分析

给定一个有向图,每个节点标记为红宝石(‘0’)或蓝宝石(‘1’)。我们需要从某些节点出发,收集宝石,使得在路径上收集的 红宝石总数蓝宝石总数 的较小值(即 min(红宝石和, 蓝宝石和))最大化。

解题思路

  1. 强连通分量(SCC)缩点
    • 使用 Tarjan 算法 求出所有强连通分量(SCC),并将每个 SCC 缩成一个点。
    • 缩点后,原图变为 有向无环图(DAG),便于后续动态规划(DP)处理。
  2. 统计 SCC 内的宝石数量
    • 对于每个 SCC,计算其包含的 红宝石数量(red[i]蓝宝石数量(blue[i]
  3. 动态规划(DP)求解最优解
    • 定义 dp[i][j] 表示在 SCC i 时,收集了 j 个蓝宝石时,能得到的 最大红宝石数量
    • 转移方程:
      • 若当前 SCC i 指向 SCC y,则更新 dp[y][j + blue[y]] = max(dp[y][j + blue[y]], dp[i][j] + red[y])
    • 最终答案:遍历所有 dp[i][j],求 min(dp[i][j], j) 的最大值。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import sys
from collections import defaultdict

def main():
    sys.setrecursionlimit(2000)
    N = 2010
    n = int(sys.stdin.readline())
    s = ' ' + sys.stdin.readline().strip()
    m = int(sys.stdin.readline())
    
    e = [[] for _ in range(N)]
    for _ in range(m):
        a, b = map(int, sys.stdin.readline().split())
        a += 1
        b += 1
        e[a].append(b)
    
    # Tarjan's SCC
    dfn = [0] * N
    low = [0] * N
    stk = []
    instk = [False] * N
    scc = [0] * N
    tot = 0
    top = 0
    cnt = 0
    
    def tarjan(x):
        nonlocal tot, top, cnt
        tot += 1
        dfn[x] = low[x] = tot
        stk.append(x)
        top += 1
        instk[x] = True
        for y in e[x]:
            if not dfn[y]:
                tarjan(y)
                low[x] = min(low[x], low[y])
            elif instk[y]:
                low[x] = min(low[x], dfn[y])
        if dfn[x] == low[x]:
            cnt += 1
            while True:
                y = stk.pop()
                top -= 1
                instk[y] = False
                scc[y] = cnt
                if y == x:
                    break
    
    for i in range(1, n + 1):
        if not dfn[i]:
            tarjan(i)
    
    red = [0] * (cnt + 2)
    blue = [0] * (cnt + 2)
    ne = [[] for _ in range(cnt + 2)]
    
    for x in range(1, n + 1):
        if s[x] == '0':
            red[scc[x]] += 1
        else:
            blue[scc[x]] += 1
        for y in e[x]:
            a = scc[x]
            b = scc[y]
            if a != b:
                ne[a].append(b)
    
    dp = [[-1] * (n + 2) for _ in range(cnt + 2)]
    for i in range(1, cnt + 1):
        if blue[i] <= n:
            dp[i][blue[i]] = red[i]
    
    for i in range(cnt, 0, -1):
        for j in range(n + 1):
            if dp[i][j] != -1:
                for y in ne[i]:
                    if j + blue[y] <= n:
                        if dp[y][j + blue[y]] < dp[i][j] + red[y]:
                            dp[y][j + blue[y]] = dp[i][j] + red[y]
    
    ans = 0
    for i in range(1, cnt + 1):
        for j in range(n + 1):
            if dp[i][j] != -1:
                ans = max(ans, min(dp[i][j], j))
    
    print(ans)

if __name__ == "__main__":
    main()

I 图书借阅

简要题意:优先队列贪心问题(待补充详细思路)

J 替换字符

简要题意:高效处理字符串的区间字符替换。

解题思路

  • 使用字典记录每个字符的位置索引。
  • 通过二分查找快速定位需要修改的区间。
  • 批量更新字符位置信息。

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import string
import bisect

s = list(input())
mp = {i: [] for i in string.ascii_lowercase}
for idx in range(len(s)):
    mp[s[idx]].append(idx)

m = int(input())
for _ in range(m):
    l, r, x, y = input().split()
    l, r = map(int, [l, r])
    mp[x].sort()
    x_l = bisect.bisect_left(mp[x], l - 1)
    x_r = bisect.bisect_right(mp[x], r - 1)
    mp[y].extend(mp[x][x_l:x_r])
    for idx in mp[x][x_l:x_r]:
        s[idx] = y
    del mp[x][x_l:x_r]

print("".join(s))
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计