POJ 2100 Graveyard Design

本文介绍了一种算法,用于找出所有连续整数段,其平方和等于给定的大数(n≤10^14)。通过使用尺取法,文章详细说明了如何在指定范围内高效地找到所有符合条件的数段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

Graveyard Design

King George has recently decided that he would like to have a new design for the royal graveyard. The graveyard must consist of several sections, each of which must be a square of graves. All sections must have different number of graves.
After a consultation with his astrologer, King George decided that the lengths of section sides must be a sequence of successive positive integer numbers. A section with side length s contains s 2 graves. George has estimated the total number of graves that will be located on the graveyard and now wants to know all possible graveyard designs satisfying the condition. You were asked to find them.

Input

Input file contains n — the number of graves to be located in the graveyard (1 <= n <= 101410^{14}1014).

Output

On the first line of the output file print k — the number of possible graveyard designs. Next k lines must contain the descriptions of the graveyards. Each line must start with l — the number of sections in the corresponding graveyard, followed by l integers — the lengths of section sides (successive positive integer numbers). Output line’s in descending order of l.

Sample Input

2030

Sample Output

2
4 21 22 23 24
3 25 26 27
Time limitCase time limitMemory limitOS
10000 ms2000 ms64000 kBLinux

题目大意

其实也就是说,给你一个数nnn,叫你找出一段连续的数,使得这段连续的数的平方和为nnn。这样的结果可能不止一个,要输出所有的结果。就样例来说,给的数字是2030,经计算可以知道,212+222+232+24=203021^2+22^2+23^2+24=2030212+222+232+24=2030,而且252+262+272=203025^2+26^2+27^2=2030252+262+272=2030,找到这样所有的连续的数段就行了。一个测试用例。

题目分析

找到一个连续的数段,用的方法应该就是尺取法了。应用尺取法的关键就是找一个连续的区间。
应用尺取法首先确定一下范围,显然应该是从1开始的(不要从0开始,从0开始,试一下样例n = 1),而在n+1\sqrt{n}+1n+1结束,也即范围区间为[1,n+1][1,\sqrt{n}+1][1,n+1]

然后,分别设出两个longlong类型(一定得是longlong 啊,我在这里卡了好久,原因下面解释)变量l,rl,rl,r,当作指针,然后当维护的和sum&gt;nsum&gt;nsum>n时,那么就sumsumsum就减去区间左端点的值的平方,同时左端点右移一位。而当sum&lt;n​sum &lt; n​sum<n时,那么右端点右移一位,再加上右端点的值的平方,这里要注意先后的顺序关系,是先加减,还是先移位。 这里要输出结果有多少个,所以要开一个数组将答案先存起来。

退出条件,自然是rrr不能右移(且sum<n,若sum>n的话,l还可以右移)时。

主要思路就是这样,但是这里有点小坑,注意到n≤1014n \le 10^{14}n1014,显然nnn应该用longlong来存储。而且同时,sum要和n来比较,所以sum应该也是一个longlong类型的变量。而r和l为什么要用longlong呢?即使输入最大的101410^{14}1014,r最大也不过是10710^7107,也没有爆int。这里坑点就在于类型转换,当sum += r*r时,r是一个int,所以r*r的结果也是int,然后由于sum是个longlong,程序这时才会将int转换成longlong,计算的结果都已经爆了,这时再转换已经没意义了。r*r大概在46300左右就会爆。所以,直接将r和l设置为longlong就行。(当然,你要在表达式前面加(longlong)强制类型转换当然也没问题。)

代码

#include <cstdio>
#include <cmath>
using namespace std;
long long int n;
int ans[1000][2];
int con = 0;
void solve(void){
  long long l = 1, r = 1;
  long long int sum = 1;
  long long int N = (long long)sqrt(n) + 1;
  while(1){
    while(sum < n){
      r++;
      sum+= (r * r);
    }
    if(r > N)
      break;
    while (sum >= n) {
      if(sum == n){
        ans[con][0] = l;
        ans[con][1] = r;
        con++;
      }
      sum -= (l * l);
      l++;
    }
  }
}
int main(int argc, char const *argv[]) {
  scanf("%lld", &n);
  solve();
  printf("%d\n", con);
  for(int i = 0; i < con; i++){
    printf("%d", ans[i][1] - ans[i][0] + 1);
    for(int j = ans[i][0]; j <= ans[i][1]; j++){
      printf(" %d", j);
    }
    printf("\n");
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值