题解:洛谷 AT_abc427_b Sum of Digits Sequence
【题目来源】
洛谷:AT_abc427_b [ABC427B] Sum of Digits Sequence - 洛谷
【题目描述】
对于一个正整数 \(x\),定义 \(f(x)\) 为 \(x\) 的十进制表示各位数字之和。例如,\(f(123) = 1 + 2 + 3 = 6\)。
定义一个无限序列 \(A = (A_0, A_1, A_2, \ldots)\),其定义如下:
- \(A_0 = 1\)
- 对于 \(i \geq 1\),\(A_i = \displaystyle\sum_{j = 0}^{i - 1} f(A_j)\)
给定一个正整数 \(N\),求 \(A_N\) 的值。
【输入】
输入从标准输入读取,格式如下:
\(N\)
【输出】
输出答案。
【输入样例】
6
【输出样例】
23
【核心思想】
-
问题分析:定义序列 \(A\) 满足 \(A_0 = 1\),\(A_i = \sum_{j=0}^{i-1} f(A_j)\)(\(i \ge 1\)),其中 \(f(x)\) 为 \(x\) 的各位数字之和。给定 \(N\),求 \(A_N\)。这是一个递推 + 数位拆分问题,关键在于理解递推关系并逐层计算,利用前缀和优化内层求和。
-
算法选择:
- 递推模拟:按定义从 \(A_0\) 递推至 \(A_N\),每个 \(A_i\) 依赖前面所有项
- 前缀和优化:注意到 \(A_i = \sum_{j=0}^{i-1} f(A_j) = A_{i-1} + f(A_{i-1})\),可将 \(O(i)\) 的内层求和优化为 \(O(1)\)
- 数位拆分:用
calc(x)函数逐位取模累加,计算 \(f(x)\)
-
关键步骤:
- 初始化:\(A_0 = 1\),\(f_0 = f(1) = 1\)
- 递推计算(\(i\) 从 \(1\) 到 \(N\)):
- 利用前缀和性质:\(A_i = A_{i-1} + f(A_{i-1})\)
- 计算 \(f_i = f(A_i)\)
- 输出 \(A_N\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \cdot \log_{10} A_N)\),递推 \(N\) 步,每步数位拆分 \(O(\log_{10} A_i)\)
- 空间复杂度:\(O(N)\),存储 \(A\) 和 \(f\) 数组
-
递推与前缀和的核心思想:
- 递推关系转化:\(A_i = \sum_{j=0}^{i-1} f(A_j) = \left(\sum_{j=0}^{i-2} f(A_j)\right) + f(A_{i-1}) = A_{i-1} + f(A_{i-1})\),将 \(O(i)\) 降为 \(O(1)\)
- 数位拆分:通过不断 \(x \% 10\) 和 \(x /= 10\) 提取各位数字,是计算数字和的标准方法
- 适用于"递推定义序列 + 前缀和优化"类问题,核心是发现相邻项的递推关系
【算法标签】
入门 #递推
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 105; // 定义最大序列长度
int n; // 序列长度
int a[N]; // 存储原始序列值
int f[N]; // 存储数字和序列值
/**
* 计算一个整数的各位数字之和
* @param x 输入整数
* @return 各位数字之和
*/
int calc(int x)
{
int res = 0;
while (x)
{
res += x % 10; // 取个位数并累加
x /= 10; // 去掉个位数
}
return res;
}
int main()
{
// 输入序列长度
cin >> n;
// 初始化前两个元素
a[0] = 1; // 第0项为1
a[1] = 1; // 第1项为1
f[0] = calc(a[0]); // 计算第0项的数字和
f[1] = calc(a[1]); // 计算第1项的数字和
// 计算序列的后续元素
for (int i = 2; i <= n; i++)
{
// 计算a[i]:前i项f值的和
for (int j = 0; j < i; j++)
{
a[i] += f[j];
}
// 计算f[i]:a[i]的各位数字之和
f[i] = calc(a[i]);
}
// 输出第n项的值
cout << a[n] << endl;
return 0;
}
【运行结果】
6
23
浙公网安备 33010602011771号