题目描述
定义一种特殊的整数序列,这种序列由连续递增的整数组成,并满足以下条件:
- 序列长度至少为 3。
- 序列中的数字是连续递增的整数(即相邻元素之差为 1),可以包括正整数、负整数或 0。
例如,[1,2,3]、[4,5,6,7] 和 [−1,0,1] 是符合条件的序列,而 [1,2](长度不足)和 [1,2,4](不连续)不符合要求。
现给定一组包含 N 个正整数的数据 A1,A2,...,AN。如果某个 Ai 能够表示为符合上述条件的连续整数序列中所有元素的和,则称 Ai 是可分解的。
请你统计这组数据中可分解的正整数的数量。
输入格式
输入的第一行包含一个正整数 N,表示数据的个数。
第二行包含 N 个正整数 A1,A2,...,AN,表示需要判断是否可分解的正整数序列。
输出格式
输出一个整数,表示给定数据中可分解的正整数的数量。
输入输出样例
输入
3
3 6 15
输出
3
说明/提示
样例说明
- Ai=3 是可分解的,因为 [0,1,2] 的和为 0+1+2=3。
- Ai=6 是可分解的,因为 [1,2,3] 的和为 1+2+3=6。
- Ai=15 是可分解的,因为 [4,5,6] 的和为 4+5+6=15。
所以可分解的正整数的数量为 3。
评测用例规模与约定
- 对于 30% 的评测用例,1≤N≤100,1≤Ai≤100。
- 对于 100% 的评测用例,1≤N≤105,1≤Ai≤109。
cs
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int is_decomposable(int S) {
long long T = 2LL * S;//S表示的是正整数,是一个和,所以可能会很大,最好要用long long类型
// 枚举 n 为 T 的约数
for (long long n = 1; n * n <= T; n++) {
if (T % n != 0) continue;
// 检查 n 作为长度
if (n >= 3) {
long long m = T / n;
if ((m-n+1) % 2 == 0) { //必须保证k是整数
return 1;
}
}
// 检查 T/n 作为长度
long long n2 = T / n;
if (n2 >= 3 && n2 != n) {
long long m2 = n; // 因为 m2 = T/n2 = n
if ((m2-n2+1) % 2 == 0) {
return 1;
}
}
}
return 0;
}
int main() {
int N;
scanf("%d", &N);
int count = 0;
for (int i = 0; i < N; i++) {
int A;
scanf("%d", &A);//输入要证明的正整数
if (is_decomposable(A)) {//用于证明是可分解的正整数,是返回1,不是返回0
count++;
}
}
printf("%d\n", count);
return 0;
}
上述代码和"最大公约数和最小公倍数"那道题目是一样的,其实比那道题目更简单。主要在这些方面是一样的:1.数论推导,推导出几个变量之间的关系,通过这些关系简化过2.is_decomposable函数中for循环和if语句几乎和那道题目是一样的。下面我给出为什么两道题目这么像......
整体思路:主要是数论推导
1.问题转化
对于给定的整数 S,检查是否存在至少 3 个连续正整数之和等于 S。
设连续正整数序列为:k, k+1, ..., k+(n-1),其中 n≥3(至少 3 个数)
S = k + (k+1) + ... + (k+n-1)
在序列 k, k+1, ..., k+(n-1) 中:
-
k 是序列的起始值(第一个数)
-
n 是序列中整数的个数
举个例子:
-
如果 n=3,表示有 3 个连续整数
-
如果 k=2,那么序列是:2, 3, 4(共 3 个数)
-
最后一个数是 k+(n-1) = 2+(3-1) = 4
2.数学公式推导
等差数列求和:S = n * [k + (k+n-1)] / 2 = n * (2k + n - 1) / 2
整理得:2S = n * (2k + n - 1)
令n = 连续整数的个数(≥3), m = 2k + n - 1,则n * m = 2S且k = (m - n + 1) / 2
因为两道题目都可以写成某两个数乘积等于一个常数的形式,而且都是一步一步推导得到的乘积形式,所以说二者真的很像。
注意!!!一定要对所有出现的变量整出个式子来表示它,然后根据这个式子才能写出它必须要满足的条件,否则光凭眼睛看,条件肯定是不全的。
3.解的判断条件
k是序列的起始值,即第一个数,所以必须是正整数,既然这样,那么(m-n+1)必须是偶数,可以自己举例,而这个偶数的实现就是可以整除2没有余数。
4.解析以下下面代码
// 检查 T/n 作为长度
long long n2 = T / n;
if (n2 >= 3 && n2 != n) {
long long m2 = n; // 因为 m2 = T/n2 = n
if ((m2-n2+1) % 2 == 0) {
return 1;
}
}
为什么要写上述这个代码呢?
例子:T=12
约数有:1, 2, 3, 4, 6, 12
当 n=1 时,n2=12
当 n=2 时,n2=6
当 n=3 时,n2=4
如果我们只检查 n≤√12≈3.46 的情况:
-
n=1: 检查 n=1(跳过,因为 n<3)
-
n=2: 检查 n=2(跳过,因为 n<3)
-
n=3: 检查 n=3
这样就会错过 n2=4, 6, 12 这些情况!而这些情况是更符合题意的。
那为什么不直接n<T呢,这样直接包含所有情况?
因为原代码的方法最多循环 √(2×10^6) ≈ 1414 次,而且一般都是按原代码的方法进行求解的。
为什么这道题目考虑到要这样做呢?
其实很难想到。所以观察一下题目要求吧。
要求找到可分解正整数,但是如果直接写,直接遍历,根本无法保证是分解成三个及以上的正整数,所以不能直接写。
如果考虑到"最大公约数和最小公倍数"问题要求,就会知道,这两道题目要求都有一点相似。"最大公约数和最小公倍数"要求满足下列两个条件的正整数个数,而这道题目要求满足下列两个条件的可分解正整数个数。"最大公约数和最小公倍数"是根据数学推论写出了n,x,y,p,q之间的关系,特别是n=y/x,n=p*q这两个关系,乘积用于写更简易的for循环条件,而除法是为了写if语句的条件以及后面的执行语句。所以本道题目也应该按上述写出一个乘积和相除的关系,这样只要根据公式满足某些条件,即可达到题意。
"最大公约数和最小公倍数"的公式是根据一个数是由因子和另一个数的乘积,所以让这两个数相乘就可以得到某个公式了,再处理化简一下就可以了,而本道题目公式就是根据等差数列公式推导出来的,2S = n * (2k + n - 1)得出了一个乘积的公式,而n就是因子,同时也是分解出来的整数个数,这样就可以写条件了,只要条件成立,即可得到符合题意的值。