洛谷
P1147连续自然数和
题目描述
对一个给定的自然数M,求出所有的连续的自然数段,这些连续的自然数段中的全部数之和为M。
例子:1998+1999+2000+2001+2002=10000,所以从1998到2002的一个自然数段为M=10000的一个解。
分析
用M除以比它小的数n,可以得到最中间的数的值(如果为*.5,为中间两个数的平均数),比如10000除以5,得到2000,2000就是中间的数的值,5是数的个数,则往两边扩展,每边都还需要2个数。起始为2000-2,终止为2000+2;如果n为偶数,则中间靠左边的数为这个值取整,比如有10个数,一共需要向左边扩展(10-2)/2个数,需要向右边扩展10/2个数。这样不好写。
另一种思路:因为是连续数的和,可以使用等差数列求和公式,如果首项是x,一共有i项,和为
i
∗
n
+
i
∗
(
i
−
1
)
2
i*n+\dfrac{{i*(i-1)}}{{2}}
i∗n+2i∗(i−1),可以枚举i,反求n:
M
=
i
∗
n
+
i
∗
(
i
−
1
)
2
M=i*n+\dfrac{{i*(i-1)}}{{2}}
M=i∗n+2i∗(i−1)
n
=
M
i
−
i
−
1
2
=
2
∗
M
−
i
2
+
i
(
分
子
)
2
∗
i
(
分
母
)
n=\dfrac{{M}}{{i}}-\dfrac{{i-1}}{{2}}=\dfrac{{2*M-i^2+i(分子)}}{{2*i(分母)}}
n=iM−2i−1=2∗i(分母)2∗M−i2+i(分子)
即如果分子可以整除分母,即为一组答案,n为第一个数字,i为数字的个数,n+i-1为最后一个数字
代码
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
int m;
int main()
{
ios::sync_with_stdio(false);
vector<pair<int, int> > ans;
while(cin>>m){
int i = 1;
int n = m/i-(i-1)/2;
while(n > 0){
if(i > 1 && (2*m-i*i+i)%(2*i) == 0){
pair<int, int> tmp;
tmp.first = n;
tmp.second = n+i-1;
ans.push_back(tmp);
}
i++;
n = m/i-(i-1)/2;
}
for(int i=(int)ans.size()-1;i>=0;i--){
cout << ans[i].first << ' ' << ans[i].second << endl;
}
}
return 0;
}