HDU 2058 The sum problem (数学+暴力)

题意:给定一个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]
", i, j);
            }
        }
        printf("
");
    }
    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]
", a, a+len);
        }
        printf("
");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/dwtfukgv/p/5526275.html