徐老师的连续正整数和
【题目来源】
瑞学堂:徐老师的连续正整数和
【题目描述】
徐老师发现,有些正整数可以表示为一个或多个连续正整数的和。
例如:
- \(9=4+5\)
- \(9=2+3+4\)
- \(15=7+8\)
- \(15=4+5+6\)
- \(15=1+2+3+4+5\)
现在,给定一个正整数 \(n\),请你帮助徐老师计算,有多少种不同的连续正整数序列(至少包含两个数),使得这些数的和恰好等于 \(n\)。
【输入】
一行,一个正整数 \(n (1≤n≤10^5)\)。
【输出】
一行,一个整数,表示和为 \(n\) 的连续正整数序列(长度至少为 \(2\))的个数。
【输入样例】
9
【输出样例】
2
【核心思想】
-
问题分析:给定正整数 \(n\),求有多少个长度至少为 \(2\) 的连续正整数序列,其和恰好等于 \(n\)。这是一个数学推导 + 枚举优化问题,关键在于利用等差数列求和公式将双重循环转化为单重循环,将时间复杂度从 \(O(n^2)\) 优化到 \(O(\sqrt{n})\)。
-
算法选择:
- 数学推导(等差数列求和):设连续序列首项为 \(a\),长度为 \(i\)(\(i \geq 2\)),则\[n = a + (a+1) + \dots + (a+i-1) = i \cdot a + \frac{i(i-1)}{2} \]变形得 \(n - \frac{i(i-1)}{2} = i \cdot a\),即 \(a = \frac{n - \frac{i(i-1)}{2}}{i}\)
- 单重枚举:枚举序列长度 \(i\),判断剩余值是否能被 \(i\) 整除且首项 \(a > 0\)
- 数学推导(等差数列求和):设连续序列首项为 \(a\),长度为 \(i\)(\(i \geq 2\)),则
-
关键步骤:
- 公式变形:由 \(n = i \cdot a + \frac{i(i-1)}{2}\),得 \(a = \frac{n - \frac{i(i-1)}{2}}{i}\)
- 枚举长度 \(i\)(从 \(2\) 开始,终止条件 \(\frac{i(i-1)}{2} < n\)):
- 计算 \(rem = n - \frac{i(i-1)}{2}\)
- 若 \(rem > 0\) 且 \(rem \bmod i = 0\),则存在合法首项 \(a = \frac{rem}{i}\),
ans++
- 终止条件分析:当 \(\frac{i(i-1)}{2} \geq n\) 时,\(rem \leq 0\),首项非正,无需继续枚举
- 输出答案 \(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(\sqrt{n})\),由 \(\frac{i(i-1)}{2} < n\) 得 \(i = O(\sqrt{n})\)
- 空间复杂度:\(O(1)\),仅使用常数变量
-
数学优化的核心思想:
- 消元降维:通过等差数列公式将"枚举首项 + 枚举末项"的双重循环,转化为"枚举长度 + 验证整除"的单重循环,利用代数变形消除一个维度
- 整除约束转存在性:将"是否存在正整数首项 \(a\)"转化为"剩余值 \(rem\) 是否为 \(i\) 的正倍数",避免实际计算 \(a\)
- 终止条件的紧界:\(\frac{i(i-1)}{2} < n\) 确保 \(rem > 0\) 且 \(a > 0\),同时限制枚举范围在 \(O(\sqrt{n})\)
- 连续和问题的通用技巧:\(n\) 表示为连续 \(i\) 个整数之和,等价于 \(n\) 存在奇因子或特定分解形式,本题通过直接枚举长度实现
- 适用于"连续序列和"类问题,核心在于利用求和公式建立长度与首项的关系,通过整除性判断解的存在性
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 将int定义为long long,避免累加和溢出
int n, ans; // n为给定的正整数,ans记录满足条件的连续正整数序列个数
signed main() // 使用signed main配合#define int long long
{
cin >> n; // 读入正整数n
// 枚举连续序列的起点a
for (int a = 1; a <= n; a++) // a为连续序列的第一个数
{
int sum = 0; // sum记录当前连续序列的累加和
// 从起点a开始向后累加,枚举序列的终点b
for (int b = a; b <= n; b++) // b为连续序列的当前累加到的数
{
sum += b; // 将b加入累加和
if (sum > n) // 如果累加和已经超过n
break; // 后续b更大,sum只会更大,直接退出内层循环
if (sum == n && b > a) // 如果累加和恰好等于n,且序列长度至少为2(b>a)
ans++; // 找到一个满足条件的连续序列,答案加1
}
}
cout << ans << endl; // 输出满足条件的连续正整数序列个数
return 0;
}
// 时间优化的版本
#include <bits/stdc++.h>
using namespace std;
#define int long long // 将int定义为long long,避免中间计算溢出
int n, ans; // n为给定的正整数,ans记录满足条件的连续正整数序列个数
signed main() // 使用signed main配合#define int long long
{
cin >> n; // 读入正整数n
// 数学推导:设连续序列长度为i,首项为a
// 则 n = a + (a+1) + ... + (a+i-1) = i*a + i*(i-1)/2
// 即 n - i*(i-1)/2 = i*a,需要该值为正且能被i整除
for (int i = 2; i * (i - 1) / 2 < n; i++) // 枚举序列长度i(至少为2)
{
int rem = n - i * (i - 1) / 2; // 计算去掉0+1+...+(i-1)后的剩余值
// rem = i*a,需要rem>0(保证首项a为正整数)且rem能被i整除
if (rem > 0 && rem % i == 0) // 如果rem为正且能被i整除,则存在合法首项a = rem/i
ans++; // 找到一个满足条件的序列,答案加1
}
cout << ans << endl; // 输出满足条件的连续正整数序列个数
return 0;
}
【运行结果】
9
2
浙公网安备 33010602011771号