高精度阶乘求和模板深度解析

在 OI(信息学竞赛)里,大家一开始都是这么写高精度的。没有花里胡哨的 vector 和函数封装,就是一套全局数组 + 多个长度变量 + 步步为营的循环 ,甚至变量名都是 a, b, c, f, m, p 这种极简风格。考场上一旦敲熟,速度极快。

既然你拿到这个代码并且觉得顺手,这题其实就是 P1303(乘法)和 P1601(加法)的缝合。

一、 代码结构全景图

这个代码的核心思路是:维护三个东西。

  1. a 数组:存放当前的 i(因为 i <= 50,所以 i 最多只有 2 位数,len_a 就是 1 或 2)。

  2. b 数组:存放 (i-1)!(上一轮算完的阶乘)。

  3. f 数组:存放最终的阶乘之和 sum。

每次循环(i 从 1 到 n)做两件事:

  • 第一步:算 i! 。把 a(当前的 i)和 b(上一轮的 (i-1)!)相乘,结果放进 c。然后把 c 的进位处理干净,再把 c 存进 b,同时记得清空 c,准备下一轮。

  • 第二步:算 sum 。把刚算出来的 i!(也就是现在的 b)加到 f 里。

二、 代码逐行深度剖析(配合你的代码)

cpp

复制代码
#include<iostream>
#include<cstring>
using namespace std;
int n, a[90], b[90], c[90], f[90];
int d=0, len_a, len_b=1, len_c=1, len_ans, m=1;
string s;

int main(){
    cin>>n;
    b[0]=1; // 关键初始化:0! = 1,且此时b代表(0)!,为后面的乘法做准备
    
    for(int i=1; i<=n; i++){
        // ----------------------------------------
        // 阶段1:把当前的 i 拆解成数组 a
        // ----------------------------------------
        len_a=0;
        int p=i;
        while(p>0){
            a[len_a++]=p%10;
            p/=10;
        }
        
        // ----------------------------------------
        // 阶段2:计算 c = a * b (也就是 i * (i-1)!)
        // ----------------------------------------
        for(int j=0; j<len_a; j++)
            for(int k=0; k<=len_b; k++)
                c[j+k]+=a[j]*b[k];
                
        // 处理 c 的进位
        for(int j=0; j<len_c; j++)
            if(c[j]>9) c[j+1]+=c[j]/10, c[j]%=10;
            
        if(c[len_c]) len_c++; // 最高位如果还有进位,长度+1
        
        // ----------------------------------------
        // 阶段3:状态转移,把 c (也就是 i!) 变成下一轮的 b
        // ----------------------------------------
        len_ans=len_b;          // 保存旧的 len_b
        len_b=len_c;            // b 的长度变成了 c 的长度
        m=max(m, len_c);        // m 记录所有阶乘中最大的长度,用于后面加法循环和去前导零
        for(int k=len_c-1; k>=0; k--) b[k]=c[k]; // c 复制给 b
        
        len_c=len_a+len_ans;    // 恢复 len_c 为初始预估长度(准备给下一轮乘法用)
        memset(c,0,sizeof(c));  // 清空 c 数组,准备计算下一个阶乘
        
        // ----------------------------------------
        // 阶段4:计算 f = f + b (也就是 sum += i!)
        // ----------------------------------------
        for(int j=0; j<m; j++){
            f[j]+=b[j];
            if(f[j]>9) f[j+1]+=f[j]/10, f[j]%=10; // 你注释里特意提醒了这点,非常棒!
        }
    }
    
    // 去前导零并倒序输出
    while(!f[m]&&m>0) m--;
    for(int i=m;i>=0;i--) cout<<f[i];
    return 0;
}

三、 这个代码里最值得夸奖的细节

  1. 乘法里 k <= len_b :因为是乘法,两数相乘的结果长度最多是 len_a + len_b,所以内层循环多跑一位(k 到 len_b)是安全的,不会越界,反而能兜住可能多出来的一位。

  2. 你的那句注释 ://进位,注意不要写成f[j+1]++,f[j]-=10;。这说明你踩过坑!在高精度加法里,进位必须用除法 :f[j+1] += f[j] / 10。如果写成 f[j+1]++,当 f[j] 是 18 或者 27 的时候,只进 1 就会错。这是非常宝贵的实战经验。

  3. memset(c,0,sizeof(c)) :每次乘法算完,立刻清空 c,这是非常标准的做法。不然下一次乘法 c 里面会残留上一次的数据,导致算错。

四、 这个代码的"风险"在哪里?

这套代码能 AC,但你在考场上用的时候,必须心里有数,它的主要风险在于长度变量太多(len_a, len_b, len_c, len_ans, m),很容易在修改的时候搞混。

  • m 的作用 :记录历史最大长度。因为阶乘是越来越大的,但如果你去遍历 f 相加时用 max(len_f, len_b),每次都要去算最大长度,很麻烦,所以直接用 m 全局记录最大长度,循环 j < m 即可。

  • len_ans 的作用 :在把 len_b 更新为 len_c 之前,先把旧的 len_b 存进 len_ans,方便重置 len_c = len_a + len_ans,为下一次乘法做准备。

下次遇到高精度乘低精度 + 高精度加法的题目,直接套这个框架。

本题目题解来源于洛谷:C_Z_C

相关推荐
周小董1 小时前
[1371]jdk管理工具
java·开发语言
Hum8le1 小时前
漏洞知识点《php伪协议》
开发语言·安全·web安全·php
青春逝如流水1 小时前
Java 中线程的创建方式:从基础到现代
java·开发语言·多线程·ai文章
智码看视界1 小时前
C++智能指针全景剖析及面试20问
开发语言·c++·深度学习·面试·内存管理·智能指针
铅笔小新z1 小时前
【C语言】深入理解指针
c语言·开发语言
Peregrine92 小时前
C++ - > 类和对象
java·c++·算法
余醉 | dtminer2 小时前
56%的保护效应藏在乳腺密度里:Nat Commun 高分中介孟德尔随机化全 R 管线拆解(附核心图)
开发语言·r语言·孟德尔随机化
2501_933923252 小时前
SpringCloud组件:Eureka服务注册与发现
java·开发语言·后端·spring cloud·eureka
染指11102 小时前
142.Agent-多Agent框架-通过Skills实现多Agent
开发语言·人工智能·python·langchain·agents