题目描述 Description
对于一个自然数M,求出所有的连续的自然数段,使得这些连续自然数段的全部数字和为M.
eg:1998+1999+2000+2001+2002=10000,所以从1998到2002的一个自然数段为M=10000的一个解。
输入描述 Input Description
一个数M
输出描述 Output Description
每行两个数,为连续自然数段的一头一尾,所有输出行的第一个数按照升序排列
样例输入 Sample Input
10000
样例输出 Sample Output
18 142
297 328
388 412
1998 2002
思路:
枚举上届,下届通过二分搜索来确定。如果下届加上届这段范围内的值超过输入数M,则缩小上届。如果小于输入的数M则扩大上届。
最后返回结果。
#include<cstdio>
#include<algorithm>
using namespace std;
// 求前n项和
inline unsigned nsum(unsigned s,unsigned e)
{
unsigned sum = 0;
unsigned n = (e - s + 1); // 项数
if(!(n%2)) // 整除
sum = n/2 * (s + e);
else
sum = (s + e)/2 * n;
return sum;
}
// 二分查找
unsigned bsearch(unsigned start,unsigned end,unsigned key) // key是最终的和
{
unsigned mid = 0;
unsigned s = start;
unsigned v = 0;
while(start < end)
{
mid = (start + end)/2;
v = nsum(s,mid);
if(v == key)
return mid; // 返回搜索的上届
else if(v > key)
end = mid - 1;
else if(v < key)
start = mid + 1; // 在start和end的范围内找 目标end
}
mid = (start + end)/2;
v = nsum(s,mid);
return v == key ? mid : -1; // 返回mid
}
// key传入的应该是上界
int main()
{
unsigned m;
unsigned s,e,sum;
scanf("%d",&m);
for(int i = 1; i <= m/2+1; i++)
{
unsigned end = bsearch(i,m/2+1,m); // 返回下届
if(end != -1)
printf("%d %d\n",i,end);
}
return 0;
}