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

在 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

相关推荐
dialect_29 分钟前
KMP算法
开发语言·c++·算法
reikocao38 分钟前
卡尔曼滤波的动态机制:P如何改变融合权重
算法
总要学点什么1 小时前
第89篇:三维特效-螺旋圆效果——让螺旋臂卷起一圈圈外扩的涟漪
开发语言·javascript·ecmascript·webgl·cesium·空间分析·1024程序员节
漂流瓶jz1 小时前
UVA-1612 猜名次 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·题解·排序·aoapc·算法竞赛入门经典
吞下星星的少年·-·2 小时前
Div.2 CF 1130B题(构造,贪心)
算法·贪心·构造
ShineWinsu2 小时前
对于Qt:QRadioButton常见属性以及QButtonGroup的解析
数据库·c++·qt·microsoft·gui·qradiobutton·qbuttongroup
傲世仙尊3 小时前
多态-虚表才活到运行期符号表早已消失
c++
开开心心_Every3 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
chenbingjie_c3 小时前
C++ 多态完全指南:从虚函数重写到虚函数表原理
开发语言·c++
是main不是漫3 小时前
【双指针——移动零】数组划分类题目的入门基石
c++·算法