1 条题解
-
0
题意分析
题目要求计算一个弹射器的总伤害。弹射器首次攻击造成伤害 ,第 次攻击造成的伤害为 (向上取整)。给定攻击次数 ,需要求出 次攻击的总伤害。输入包含多个测试用例,每个用例为两个正整数 和 (),输入以两个 0 结束。输出每个测试用例的总伤害。
关键挑战:
- 和 最大可达 ,直接遍历 从 1 到 计算 会超时(时间复杂度 )。
- 需要高效算法:利用数学转换和整除分块技术(也称为调和级数技巧),将问题转化为计算 的和,将时间复杂度优化到 。
数学推导:
- (对于正整数 和 )。
- 总伤害 $S = \sum_{K=1}^{N} \lceil D/K \rceil = \sum_{K=1}^{N} \left( \lfloor (D-1)/K \rfloor + 1 \right) = \sum_{K=1}^{N} \lfloor (D-1)/K \rfloor + N$。
- 令 :
- 如果 ,则 ,(因为每个 )。
- 如果 ,则 ,求和 只需计算到 ,因为在 时,。
解题思路
- 处理边界情况:
- 如果 ,直接返回 。
- 如果 ,计算 和 。
- 整除分块计算和 :
- 整除分块思想: 的值在多个连续的 上相同,这些区间(块)可以快速枚举。
- 算法步骤:
- 初始化 和部分和 。
- 当 :
- 计算 (当前块的值)。
- 计算块的右端点 (此范围内 )。
- 计算块内元素个数 。
- 累加块贡献 。
- 更新 处理下一个块。
- 时间复杂度:,因为块数约为 。对于 ,,高效可行。
- 总伤害计算:
- 。
- 多测试用例处理:
- 读取每个测试用例 ,直到遇到 。
- 使用
long long
类型防止溢出(最大总伤害 ,在long long
范围内)。
C++ 代码实现
#include <iostream> using namespace std; long long computeTotal(long long D, long long N) { if (D == 1) { return N; // ceil(D/K)=1 for all K } long long M = D - 1; long long up = min(N, M); // Only sum K from 1 to min(N, M) long long T = 0; if (M > 0) { long long i = 1; while (i <= up) { long long v = M / i; // floor(M/i) long long j = min(M / v, up); // Right endpoint of the block long long count = j - i + 1; // Number of elements in block T += v * count; // Sum of this block i = j + 1; // Move to next block } } return T + N; // Total damage } int main() { ios::sync_with_stdio(false); cin.tie(0); long long D, N; while (cin >> D >> N) { if (D == 0 && N == 0) break; cout << computeTotal(D, N) << '\n'; } return 0; }
- 1
信息
- ID
- 1668
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者