题目描述
给定1到n共n个数,你需要找出连续的几个数使得其和为m,显然这样的方案有很多,请将每种方案都输出,输出方法请参考样例。
输入
第一行两个整数n和m。
输出
输出有多行,每行表示一种合法的方案,采用闭区间的方式给出方案([a,b]表示a到b这几个连续的整数),当有多种方案时按照a由小到大输出。
样例输入 Copy
20 15
样例输出 Copy
[1,5] [4,6] [7,8] [15,15]
提示
样例说明
1+2+3+4+5 = 15
4+5+6 = 15
7+8 = 15
15 = 15
【数据规模】
对于30%的数据,n,m<=100。
对于60%的数据,n,m<=1000000。
对于100%的数据,n,m<=10^12。
题解:
最直观暴力的方法就是双层循环,穷举出所有和等于M的连续区间,时间复杂度是O(n^2)然而对于 10^12次方的数据量,是不可能以O(n^2)复杂度的算法,在限定时间 1s内解完的。。
对于一般情况来说,执行的指令达到10^8量级,在1s时间内就已经很悬了。
所以~我们来看看其他方法:
先忽略最大值n的条件,直接看如何求出和为m的连续区间。
思路肯定要放在如何把m拆成多个连续数的和。
如果要把m拆成一个数,不用说,m肯定还是m。我们自然得到了区间[M,M]。
如果要把m拆成两个数,变成区间[x,x+1]呢?
首先要看看m能不能被拆成两个数,
拆成两个数,自然想到要将m/2,
如果m是偶数 则m/2为一个整数,m=m/2+m/2。
而我们要将m以m=x+(x+1)(x为整数) 的形式表示出来,然而在m是偶数的情况 下,观察可以发现(方程无整数解?)这是不可能的。
当m为奇数时,m/2=(m-1)/2+0.5;
m=(m-1)/2+(m-1)/2+1;可以发现,m可以以两个连续数的和表示出来!以区间表 示就是[(m-1)/2,(m-1)/2+1];
那如果要将m拆为三个数呢?
我们直接m/3,若有整数解的话,将会时三个相同的数 x,x,x;我们从左边的x拿走一个1加 到右边的x上,变为x-1 x x+1。是三个连续的数!
所以,如果m能被3整除,则其一定能被分为三个连续的数的和。同时,反过来也可证明 三个连续数的和必定会被3整除
我们稍微步子迈大一点。
如果要将m拆为(奇数x)个数?
例如拆为5个数,设x=n/5,那所拆成的5个数可以用 x-2 x-1 x x+1 x+2 表示
多次举例,归纳可得
所以,m必须要被x整除,才可拆为x个数 (x为奇数), 其区间表示为[m/x-(x-1)/2,m/x-(x-1)/2]
那么偶数个数呢?
例如4个数!16/4=4 对于 4 4 4 4 这四个数来说,我们采取一些手段,在变化最小的情况下,让其变成连续的数。最左边的4拿走2 中间靠左的4拿走1 最右边的4加上1.
变为了 2 3 4 5 其和为14。我们总共从4 4 4 4里面拿走了2.
再看一下 5 5 5 5 如果要使其变成连续的数,发现我们也需要从5 5 5 5里面拿走2.
多次举例发现。
(x为偶数) 如果要将x个相同的数变为 x个连续数。则需用从x个相同的数中取走(x/2)
那么可发现,x个连续数的和与x个相同数的和有着关系:
总结一下,发现m如果可以被分为x个连续的数的和。
则(m+x/2)一定要可被x整除,而其拆出来的区间应该表示方法..(懒)在下面的代码部分中。
于是 我们得到了如下规律
如果要将m拆为(奇数x)个连续数的和,则m一定要可被x整除。
如果要将m拆为(偶数x)个连续数的和,则(m+x/2)一定要可被x整除
有了上述规律发现后,再用代码实现就不成问题了 :)
同时要注意区间的端点值需要小于等于n,这个方法的时间复杂度为不会算
//循环次数为1+2+3+……+i>=n时i的取值;可得到i^2-i=2n的表达式。
当n十分十分大时,i可忽略不计,原式为i^2=2n。
可得到时间复杂度应为O(n^1/2)?//
如有纰漏,请多多指正!!
#include <cstdio>
int main(){
int q=0;
unsigned long long int n,m,i,s=0;
unsigned long long int a[1000]; //因为题目要求a从小到大输出,所以开了两个数组存储端点
unsigned long long int b[1000];
scanf("%llu %llu",&n,&m);
for(i=1;;i++){ //i为要将m拆成的数的个数。
s+=i;
if(i&1){
if(m%i==0){
if(m/i+i/2<=n){
a[q]=m/i-i/2;b[q++]=m/i+i/2;
}
}
}
else{
if((m+i/2)%i==0){
if((m+i/2)/i+i/2-1<=n){
a[q]=(m+i/2)/i-i/2;b[q++]=(m+i/2)/i+i/2-1;
}
}
}
if(s>=m) //当1+2+3+...+i>=m时,肯定无法再将m拆为更多的数了。
break;
}
while(q--){
printf("[%llu,%llu]\n",a[q],b[q]);
}
return 0;
}