数列
jzoj 2752
题目大意:
给你一个正整数n(有多组数据),让你把它分为一个连续的正整数列之和(长度大于1),然你求着个数列最短的长度,如果这个序列不存在,那输出-1
输入样例
9
2
输出样例
2
-1
数据范围
对于所有数据,n≤263n≤26^3n≤263。
解题思路
这道题我们要分类讨论
我们先说奇数:
1很显然是不可能的就是-1
大于1的整数可以分为n/2n/2n/2和n/2+1n/2+1n/2+1
然后是偶数:
我们又要 分类讨论:
我们设这个序列是a1,a2...an−1,an,X,bn,bn−1...b2,b1a_1,a_2...a_{n-1},a_n,X,b_n,b_{n-1}...b_2,b_1a1,a2...an−1,an,X,bn,bn−1...b2,b1
X是中间数,那中间数×××长度等于输入的N
如果序列长度是偶数
那X就是一个小数(x.5),且bn=an+1b_n=a_n+1bn=an+1
我们就把a1a_1a1和b1b_1b1,ana_nan和bnb_nbn分为一组,以此类推
那每一组都是奇数,就没有偶数因子
我们把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;
}