题意:给定一个N和M,N表示从1到N的连续序列,让你求在1到N这个序列中连续子序列的和为M的子序列区间。
析:很明显最直接的方法就是暴力,可是不幸的是,由于N,M太大了,肯定会TLE的。所以我们就想能不能优化一下,找一个范围。想到这是一个连续的序列而且是从1开始的,这不就是一个等差数列么,公差是1罢了。由求和公式得Sn = (a1+an) * n / 2;所以说n最大就是sqrt(M*2)(想一想为什么),因为a1+an 一定是大于n的。如果我们取区间的和,那么Sn = (ai+aj) * (j-i+1)/2;以上我们可得到一个方程,i+j = M/n(当然n|M),j-i+1 = n;所以我们可以解出i和j,其他的就简单了从n到1暴一遍就OK。
当我做完后我又看了网上的题解,我个去,写的比我简单多了。。。
他们是这么说的,等差数列的运用。Sn = (a1+an) * n / 2 = (a1 + a1 + (n - 1) * d)*n/2。
解题公式变形:(a+a+len)*(len+1)/2 = m => a = m/(len+1)-len/2 (m是已知条件,len的最大值为sqrt(2*m))。
代码如下:
这是我写的代码:
#include <iostream> #include <string> #include <vector> #include <algorithm> #include <cstdio> #include <cmath> using namespace std; int main(){ int n, m; while(scanf("%d %d", &n, &m)){ if(!n && !m) break; m <<= 1; int len = (int)sqrt(m) + 1; while(--len){ if(n > len && m % len == 0){ if((len + m / len - 1) % 2) continue; int j = (len + m / len - 1) / 2; if(j > n || j < 0) continue; int i = m / len - j; if(i > n || i < 0) continue; if(i > j) swap(i, j); printf("[%d,%d]\n", i, j); } } printf("\n"); } return 0; }
下面是题解的代码:
#include <cstdio> #include <cmath> using namespace std; int main() { int n, m, a, len; while (scanf("%d%d", &n, &m) && (n || m)) { len = (int)sqrt(2*m); while (len--) { a = m / (len + 1) - len / 2; if ((2*a+len) * (len+1) / 2 == m) printf("[%d,%d]\n", a, a+len); } printf("\n"); } return 0; }