1 条题解
-
0
题目分析
本题要求计算 的最后一位非零数字,其中 可达 ,无法直接计算阶乘。
数学原理
末尾零的产生
末尾的零来自因子 和 的配对。由于 的个数多于 ,末尾零的个数由 的因子个数决定。
最后非零数字的计算
去掉所有 和 的因子对后,剩余因子的乘积的个位数即为所求。
已知结论:设 为 的最后非零数字,则:
$$L(n) = L\left(\left\lfloor \frac{n}{5} \right\rfloor\right) \times L(n \bmod 5) \times 2^{\lfloor n/5 \rfloor} \bmod 10 $$其中 ,,,,。
代码解析
#include <bits/stdc++.h> using namespace std; char s[100005]; int n, a[100005]; // 预处理的查找表:f[k] = L(n) 对于 n=0 到 19 int f[32] = {1, 1, 2, 6, 4, 2, 2, 4, 2, 8, 4, 4, 8, 4, 6, 8, 8, 6, 8, 2}; int ans; int main() { for (int i = 1; i <= 5; i++) { ans = 1; scanf("%s", s); n = strlen(s); // 将字符串转换为大整数数组(逆序存储) for (int i = 0; i < n; i++) a[n - i - 1] = s[i] - '0'; // 循环处理,每次相当于 n = n/5 while (n > 1) { // 去除前导零 while (a[n - 1] == 0) n--; // 计算 L(n mod 20) × 2^(n/5) mod 10 // a[1]%2*10 + a[0] 相当于 n mod 20 ans = ans * f[a[1] % 2 * 10 + a[0]] % 10; // 计算 n/5(大整数除以5) int x = 0; for (int j = n - 1; j >= 0; j--) { x = x * 10 + a[j]; a[j] = x / 5; x %= 5; } } // 最终结果:ans × L(n) mod 10,其中n此时小于5 printf("%d\n", ans * f[a[0]] % 10); } return 0; }
预处理表说明
f[0..19]存储了 的最后非零数字:- ...
实际上 对于 到 。
算法流程
- 输入处理:将 作为字符串读入,转换为大整数数组(逆序存储)
- 循环计算:
- 去除大整数前导零
- 计算 $L(n \bmod 20) \times 2^{\lfloor n/5 \rfloor} \bmod 10$ 并累积到答案
- 将 除以 继续下一轮
- 终止条件:当 时,直接查表得到
- 输出结果:所有累积结果的乘积模
关键步骤详解
大整数处理
- 数组
a逆序存储数字,a[0]是个位,a[1]是十位,依此类推 - 大整数除以 通过模拟竖式除法实现
核心计算公式
在循环中执行的实际上是:
$$\text{ans} = \text{ans} \times L(n \bmod 20) \times 2^{\lfloor n/5 \rfloor} \bmod 10 $$其中 的个位数通过预处理表隐含处理。
复杂度分析
- 时间复杂度: 每次循环 除以 ,循环次数为 的 进制位数
- 空间复杂度: 存储大整数数组
示例验证
的计算过程:
- 初始:,
ans=1 - 第一轮:,,
ans=1×8=8 - 第二轮:,循环结束
- 最终:
ans × f[3] = 8 × 6 = 48 → 8
输出 ,与样例一致。
算法优势
- 高效处理大数:避免直接计算阶乘,适用于 极大的情况
- 数学优化:利用模运算和预处理表加速计算
- 正确性保证:基于严格的数论公式推导
该解法充分利用了阶乘最后非零数字的数学性质,通过递归分解和预处理技术,实现了对大数情况的高效计算。
- 1
信息
- ID
- 4290
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 10
- 已通过
- 1
- 上传者