数 列 ( s e q u e n c e ) 数列(sequence) 数列(sequence)
题目链接:jzoj 2752
题目
把一个正整数分成一列连续的正整数之和。这个数列必须包含至少两个正整数。你需要求出这个数列的最小长度。如果这个数列不存在则输出 − 1 -1 −1。
输入
每行包含一个正整数
n
n
n。
每个文件包含多行,读入直到文件结束。
输出
对于每个 n n n,输出一行,为这个数列的最小长度。
输入样例
9
2
输出样例
2
-1
数据范围
对于所有数据, n ≤ 2 63 n≤2^{63} n≤263。
思路
一道思维题。
首先,我们可以知道,当
n
n
n为
1
1
1或
2
2
2的时候,答案是
−
1
-1
−1。
然后,我们可以通过观察数据与打表发现,当
n
n
n为奇数的时候(除了
1
1
1),答案都为
2
2
2。
接着,我们再来处理
n
n
n为偶数的情况,可以分两种情况讨论:
- 数列的长度为偶数,则数列的平均值是一个小数( ∗ ∗ ∗ . 5 ***.5 ∗∗∗.5的样子)。而我们要让这个数列的数尽可能少,这个数列的和有不变,就只能让数列的平均值尽可能的大。我们可以找出 n n n最大的奇数因子,而长度就为: n / 最 大 的 奇 数 因 子 ∗ 2 n\ /\ 最大的奇数因子\ *\ 2 n / 最大的奇数因子 ∗ 2。
- 数列的长度为奇数,那么数列的平均值就是一个整数,我们只要直接枚举长度,找到最小的奇数长度就可以了。
然后那个小答案就是那个,若是两个都没有答案就输出 − 1 -1 −1。
代码
#include<cstdio>
#define rr register
#define ll long long
using namespace std;
ll n;
int main() {
while (scanf("%lld", &n) == 1) {//读入
if (n < 3) {//n为1或者2的话都是-1
printf("-1\n");
continue;
}
if (n % 2 == 1) {//其它的如果是奇数答案都为2
printf("2\n");
continue;
}
ll nn = n, ans = 100000000000000;
while (nn % 2 == 0) nn >>= 1;//求出这个数的最大奇数因子
if ((nn + 1) / 2 - n / nn >= 0) {//求出偶数长度中最小的长度
ans = n / nn * 2;
if ((nn + 1) / 2 - n / nn == 0)
ans--;
}
for (rr ll int i = 3; i <= ans && i * i <= n && i <= nn; i += 2)//找出有没有奇数长度更小的
if (n % i == 0) {
ans = i;
break;
}
if (ans == 100000000000000) printf("-1\n");//没有找到序列
else printf("%lld\n", ans);//输出
}
return 0;
}