【数学】数列(jzoj 2752)

数列

jzoj 2752

题目大意:

给你一个正整数n(有多组数据),让你把它分为一个连续的正整数列之和(长度大于1),然你求着个数列最短的长度,如果这个序列不存在,那输出-1

输入样例

9
2

输出样例

2
-1

数据范围

对于所有数据, n 2 6 3 n≤26^3

解题思路

这道题我们要分类讨论
我们先说奇数:
1很显然是不可能的就是-1
大于1的整数可以分为 n / 2 n/2 n / 2 + 1 n/2+1
然后是偶数:
我们又要 分类讨论:
我们设这个序列是 a 1 , a 2 . . . a n 1 , a n , X , b n , b n 1 . . . b 2 , b 1 a_1,a_2...a_{n-1},a_n,X,b_n,b_{n-1}...b_2,b_1
X是中间数,那中间数 × × 长度等于输入的N
如果序列长度是偶数
那X就是一个小数(x.5),且 b n = a n + 1 b_n=a_n+1
我们就把 a 1 a_1 b 1 b_1 a n a_n b n b_n 分为一组,以此类推
那每一组都是奇数,就没有偶数因子
我们把N分解质因子,奇数全放在每一组处,所有2放在长度
因为2不能放在每一组处,而如果把某些奇数放在长度处,那把2放在每一组处,就可以行成更短的奇数序列了,那就不可能是最优的
然后我们枚举奇数的长度(具体实现看代码)
每一次求出长度我们还要判断有没有小于等于0(就看中间数是否大于长度的一半,因为要从中间数往左一半的长度)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll n, m, i, ans;
int main()
{
	while(cin>>n)
	{
		ans = -1;
		if (n > 1 && (n&1)) ans = 2;//奇数
		if (!(n&1))//偶数
		{
			m = n;
			while(!(m&1)) m >>= 1;//提取奇数因子总和
			if (m / 2 >= n / m) ans = n / m * 2;//每一组是m,判断有没有出界就用前一个数m/2(后一个数是m/2+1)
												//有n/m组,一组有两个数,长度是n/m*2,那n/m就是长度一半
			i = 3;//枚举奇数
			for (; i * i <= m && (ans == -1 || i < ans); i += 2)//要判断是否枚举的更优,且只枚举到sqrt(m),因为如果大于sqrt(m)且有答案,那长度和中间数的奇数因子,掉反那会更优
				if (m % i == 0 && n / i >= (i + 1) / 2)//能整除,i是长度,中间数要把偶数因子也算进去,就是n/i,因为中间数也有一个长度,所以要加一
					ans = i;
			if (m != 1 && n / m >= (m + 1) / 2 && (ans == -1 || m < ans))//以m为长度的情况
				ans = m;
		}
		printf("%lld\n", ans);
	}
	return 0;
}
发布了334 篇原创文章 · 获赞 57 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/ssllyf/article/details/104176659