dotcpp 1258: 连续自然数和
dotcpp 1258: 连续自然数和
时间限制: 1s
内存限制: 128MB
题目描述
对一个给定的自然数M,求出所有的连续的自然数段(连续个数大于1),这些连续的自然数段中的全部数之和为M。
例子:1998+1999+2000+2001+2002 = 10000,所以从1998到2002的一个自然数段为M=10000的一个解。
输入格式
包含一个整数的单独一行给出\(M\)的值(10 <= M <= 2000000)。
输出格式
每行两个自然数,给出一个满足条件的连续自然数段中的第一个数和最后一个数,两数之间用一个空格隔开。
所有输出按照起点从小到大升序排列,题目保证输入数据至少存在一组合法解。
样例输入
10000
21334
2000000
样例输出
18 142
297 328
388 412
1998 2002
5332 5335
923 2202
2888 3512
7685 7940
15938 16062
79988 80012
399998 400002
dotcpp 1258 连续自然数和
一、数学公式推导
设连续自然数首项为 \(a\),项数为 \(k\)(\(k \ge 2\)),总和为 \(M\)
根据等差数列求和:
\[M = \frac{k}{2} \times [2a + (k-1)]
\]
变形整理求首项:
\[2M = k(2a + k - 1)
\]
\[2a = \frac{2M}{k} - k + 1
\]
\[a = \frac{\dfrac{2M}{k} - k + 1}{2}
\]
二、约束条件
- \(k\) 是 \(2M\) 的约数
- 首项 \(a\) 必须是正整数
- 项数 \(k \ge 2\)
- 由 \(a>0\) 推出范围:
\[\dfrac{2M}{k}-k+1>0 \implies k^2+k < 2M
\]
项数上限:\(k < \sqrt{2M}\)
三、最优枚举策略
- 只枚举项数 k,范围:\(2 \le k \le \sqrt{2M}\)
- 第一步判断:\(2M\) 能否整除 \(k\),不能直接跳过
- 能整除则代入公式算出首项 \(a\)
- 判断 \(a\) 是否为正整数(分子必须为偶数且大于0)
- 满足条件:末尾数 \(= a + k - 1\),记录
(a, 末尾数)
四、输出排序规则
枚举得到的答案天然首项从小到大,直接按遍历顺序输出即可,无需额外排序。
五、复杂度分析
- 枚举范围仅到 \(\sqrt{2M}\)
- 最大数据 \(M=2\times10^6\),\(\sqrt{4000000}=2000\),最多只循环2000次
- 时间复杂度:\(O(\sqrt{M})\),远超暴力枚举起点,1s极限数据轻松通过
六、易错点总结
- 必须保证项数 \(k\ge2\),排除单个数字
- 区分整除条件 + 首项为整数两个判断
- 不要枚举起点,枚举项数才是本题最优解法
- 算出合法区间直接保存,遍历完统一输出
代码实现
点击查看代码
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
public class 连续自然数和 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int M;
M = sc.nextInt();
//数组属于引用类型,不能在这里提前创建数组,否则会出错
// int[] num = new int[2];
List<int[]> list = new ArrayList<>();
int start = 2;
int end = (int) Math.sqrt(2 * M);
for (int k = start; k <= end; k++) {
int a = -1, b = -1;
if (2 * M % k == 0) {
if ((2 * M / k - k + 1) % 2 == 0 && (2 * M / k - k + 1) > 0) {
a = (2 * M / k - k + 1) / 2;
} else {
continue;
}
} else {
continue;
}
b = a + k - 1;
if (a > 0 && b > 0) {
//必须在这里反复创建数组,否则会出错
int[] num = new int[2];
num[0] = a;
num[1] = b;
list.add(num);
}
}
// list.sort(Comparator.comparingInt(a -> a[0]));
// for (int[] n : list) {
// System.out.println(n[0] + " " + n[1]);
// }
for (int i = list.size() - 1; i >= 0; i--) {
System.out.println(list.get(i)[0] + " " + list.get(i)[1]);
}
}
}
浙公网安备 33010602011771号