题目2706:蓝桥杯2022年第十三届决赛真题-六六大顺
本文最后更新于157 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

突然想到好久都没有做算法的讲解了,这次更新打算用一道通过率较低的题目来讲解一下算法

题目分析

数列A的规律:

  • a₁ = 6
  • a₂ = 66
  • a₃ = 666
  • aᵢ = 10 × aᵢ₋₁ + 6

数列B的规律:

  • bᵢ = aᵢ × aᵢ
  • b₁ = 6 × 6 = 36
  • b₂ = 66 × 66 = 4356

要求: 计算 b₁ + b₂ + … + bₙ

关键问题

当 n = 10,000,000 时:

  • aₙ 会非常大(约10⁷位数)
  • bₙ 会更大
  • 无法用常规整数存储!

需要用字符串或大数运算处理。

于是我的第一版代码就是

n = int(input())

# 用字符串模拟大数运算
a = "6"  # a1 = 6
total = "0"  # 总和

def string_add(x, y):
    """字符串大数加法"""
    # 对齐位数
    max_len = max(len(x), len(y))
    x = x.zfill(max_len)
    y = y.zfill(max_len)
    
    result = []
    carry = 0
    for i in range(max_len - 1, -1, -1):
        digit_sum = int(x[i]) + int(y[i]) + carry
        result.append(str(digit_sum % 10))
        carry = digit_sum // 10
    if carry:
        result.append(str(carry))
    
    return ''.join(reversed(result))

def string_multiply(x, y):
    """字符串大数乘法"""
    if x == "0" or y == "0":
        return "0"
    
    result = [0] * (len(x) + len(y))
    
    for i in range(len(x) - 1, -1, -1):
        for j in range(len(y) - 1, -1, -1):
            mul = int(x[i]) * int(y[j])
            p1, p2 = i + j, i + j + 1
            total = mul + result[p2]
            result[p2] = total % 10
            result[p1] += total // 10
    
    # 转为字符串,去掉前导零
    result_str = ''.join(map(str, result)).lstrip('0')
    return result_str if result_str else "0"

for i in range(1, n + 1):
    # 计算 b_i = a_i * a_i
    b = string_multiply(a, a)
    
    # 累加到总和
    total = string_add(total, b)
    
    # 计算下一个 a_{i+1} = a_i * 10 + 6
    if i < n:
        a = a + "6"  # 直接在末尾加6即可

print(total)

但有两个细节会让程序在 n = 10^7 时 超时 / 超内存:
字符串越来越长ai 的长度是 i 位,平方后长度 ≈ 2i。当 i = 10^7 时,单条字符串就有 2×10^7 字符,再乘上 Python 字符串不可变,每次拼接、乘法都会拷贝 O(len) 字节,总操作量约为
Σ_{i=1}^{n} O(i) = O(n²) ≈ 5×10^13 次字符搬运,远远超出 3 s 限制。

大数加法也越加越大
总和长度最后达到 2n = 2×10^7 位,每加一次都要遍历这么长,总时间又是 O(n²)。
因此“纯字符串模拟”只能拿到 50 分(n ≤ 10^5)左右,n = 10^7 必定 TLE/MLE。

所以把代码推翻重写

数学分析

数列A的通项公式:

aᵢ = 666…6 (i个6)
= 6 × (10^(i-1) + 10^(i-2) + … + 10^0)
= 6 × (10^i - 1) / 9
= 2 × (10^i - 1) / 3

数列B的通项公式:

bᵢ = aᵢ² = [2 × (10^i - 1) / 3]²
= 4 × (10^i - 1)² / 9
= 4 × (10^(2i) - 2×10^i + 1) / 9

求和公式:

Sₙ = Σ(i=1 to n) bᵢ
= Σ(i=1 to n) [4×(10^(2i) - 2×10^i + 1) / 9]
= 4/9 × [Σ10^(2i) - 2×Σ10^i + n]

利用等比数列求和:

Σ10^i = (10^(n+1) - 10) / 9
Σ10^(2i) = (10^(2n+2) - 100) / 99

Python(数学公式)

n = int(input())

def fast_power_mod(base, exp, mod):
"""快速幂计算 (base^exp) % mod"""
result = 1
base = base % mod
while exp > 0:
if exp & 1:
result = (result * base) % mod
base = (base * base) % mod
exp >>= 1
return result

# 计算 S_n = Σ b_i
# 使用推导的公式
# S_n = 4/9 * [(10^(2n+2)-100)/99 - 2*(10^(n+1)-10)/9 + n]

# 但由于涉及分数,我们用模运算的逆元或者直接大数计算
# Python 3.8+ 可以用 int 直接处理,但需要优化

from decimal import Decimal, getcontext

# 设置足够的精度
getcontext().prec = 50

def compute_sum(n):
# 使用公式直接计算,避免循环
# S_n = 4/9 * [((10^(2*n+2) - 100) / 99) - 2 * ((10^(n+1) - 10) / 9) + n]

two_pow = Decimal(10) ** (2 * n + 2)
one_pow = Decimal(10) ** (n + 1)

term1 = (two_pow - Decimal(100)) / Decimal(99)
term2 = Decimal(2) * (one_pow - Decimal(10)) / Decimal(9)
term3 = Decimal(n)

sum_b = Decimal(4) / Decimal(9) * (term1 - term2 + term3)

return int(sum_b)

result = compute_sum(n)
print(result)

但是我发现还是会时间/内存超限

所以最后我选择使用C++来实现

#include <cstdio>

long long ans[29000000], n;

int main(){
    scanf("%lld", &n);
    
    ans[1] = n * 6;
    
    for(int i = 2; i <= n + 1; i++){
        ans[i] = 3 + (n-i+1) * 5 + (i-2) / 2 * 4;
    }
    
    for(int i = 2 * n, j = 2; i >= n + 2; i--, j++){
        ans[i] = j / 2 * 4;
    }
    
    // 进位
    for(int i = 1; i < 2 * n; i++){
        ans[i+1] += ans[i] / 10;
        ans[i] %= 10;
    }
    
    // 输出(注意输出范围)
    for(int i = 2 * n; i >= 1; i--){
        if(ans[i] != 0 || i != 2*n)  // 跳过前导零
            putchar(ans[i] + '0');
    }
    putchar('\n');
    
    return 0;
}

算法分析

这个解法使用了一个数学规律,直接计算每一位的值,而不是逐个计算 aᵢ 和 bᵢ。

让我验证一下这个规律:

对于 n = 3(样例):

  • 结果应该是:447948
  • 数组长度:2 × n = 6

计算过程:

ans[1] = n × 6 = 3 × 6 = 18

ans[2] = 3 + (3-2+1) × 5 + (2-2)/2 × 4 = 3 + 5 + 0 = 8
ans[3] = 3 + (3-3+1) × 5 + (3-2)/2 × 4 = 3 + 5 + 4 = 12
ans[4] = 3 + (3-4+1) × 5 + (4-2)/2 × 4 = 3 + 0 + 4 = 7

ans[6] = 2/2 × 4 = 4 (j=2)
ans[5] = 3/2 × 4 = 4 (j=3, 整除)

进位处理前:[18, 8, 12, 7, 4, 4]

进位处理后:

ans[1] = 18 % 10 = 8, carry = 1
ans[2] = 8 + 1 = 9, carry = 0
ans[3] = 12 % 10 = 2, carry = 1
ans[4] = 7 + 1 = 8, carry = 0
ans[5] = 4, carry = 0
ans[6] = 4, carry = 0

但是如果重新检查代码逻辑

ans[1] = n * 6; // ans[1] = 18

for(int i = 2; i <= n + 1; i++){
ans[i] = 3 + (n-i+1) * 5 + (i-2) / 2 * 4;
}

对于 n=3:

  • ans[2] = 3 + (3-2+1)×5 + 0 = 3 + 5 = 8
  • ans[3] = 3 + (3-3+1)×5 + 4 = 3 + 5 + 4 = 12
  • ans[4] = 3 + (3-4+1)×5 + 4 = 3 + 0 + 4 = 7
for(int i = 2 * n, j = 2; i >= n + 2; i--, j++){
ans[i] = j / 2 * 4;
}

对于 n=3, 从 i=6 到 i=4:

  • i=6, j=2: ans[6] = 2/2×4 = 4
  • i=5, j=3: ans[5] = 3/2×4 = 4 (整除)
  • i=4, j=4: ans[4] = 4/2×4 = 8 (覆盖了之前的7!)

所以实际数组是:[18, 8, 12, 8, 4, 4]

进位后:

  • i=1: ans[1]=8, ans[2]+=1 → 9
  • i=2: ans[2]=9, ans[3]+=1 → 13
  • i=3: ans[3]=3, ans[4]+=1 → 9
  • i=4: ans[4]=9
  • i=5: ans[5]=4
  • i=6: ans[6]=4

结果:[8, 9, 3, 9, 4, 4] → 449389

最后的代码

#include <cstdio>

long long ans[29000000], n;

int main(){
    scanf("%lld", &n);
    
    ans[1] = n * 6;
    
    for(int i = 2; i <= n + 1; i++){
        ans[i] = 3 + (n-i+1) * 5 + (i-2) / 2 * 4;
    }
    
    for(int i = 2 * n, j = 2; i >= n + 2; i--, j++){
        ans[i] = j / 2 * 4;
    }
    
    // 进位
    for(int i = 1; i < 2 * n; i++){
        ans[i+1] += ans[i] / 10;
        ans[i] %= 10;
    }
    
    // 输出(注意输出范围)
    for(int i = 2 * n; i >= 1; i--){
        if(ans[i] != 0 || i != 2*n)  // 跳过前导零
            putchar(ans[i] + '0');
    }
    putchar('\n');
    
    return 0;
}

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇