蓝桥杯国赛题:123

本题为2021年蓝桥杯国赛题。

题目描述
小蓝发现了一个有趣的数列,这个数列的前几项如下:

1, 1, 2, 1, 2, 3, 1, 2, 3, 4,⋯

小蓝发现,这个数列前 1 项是整数 1,接下来 2 项是整数 1 至 2,接下来 3 项是整数 1 至 3,接下来 4 项是整数 1 至 4,依次类推。

小蓝想知道,这个数列中,连续一段的和是多少?

输入描述
输入的第一行包含一个整数 T,表示询问的个数。

接下来 T 行,每行包含一组询问,其中第 i 行包含两个整数 l 和 r,表示询问数列中第 l​ 个数到第 r 个数的和。

输出描述
输出 T 行,每行包含一个整数表示对应询问的答案。

题解:

将数列看成三角矩阵,即第1行只有数:1,第2行只有数:1,2,第3行只有数:1,2,3,如下:

1

1 2

1 2 3

1 2 3 4

……

1 2 3 4 5 …

显然每一行的元素个数是符合等差数列的,即第i行的元素个数是i(说明该行是完整的)。

第一步:获取位置

定义一个函数专门获取该数列的第n项所在的行和列(假设是第x行,第y列),那么对于前x-1行,每一行的数都是完整的,只有在第x行,只有y个数(可能不完整也可能完整)。显然,如果y=x则说明第x行也是完整的,但如果y<x则说明第x行是不完整的排在该行后面有部分数是没有的。因此对于每一行的元素个数是满足等差数列的,可以利用等差数列的求和公式反推其是到第几行是完整的(一元二次方程的求根公式向下取整),假设利用方程解出是t,则可以推出原数列的第n项所在的位置(行,列):x=t+1, y=n-t(t+1)2 。

第二步:求和

题目要求对于每一次查询,都需要输入 l 和 r ,需要输出原数列的第 l 个数到第 r 个数的和。因此我们可以先分别计算出第 l 个数,和第 r 个数所在的位置(行,列),再对其中间的各数求和即可。对于求和,需要考虑一种特殊情况,即数列中的第 l 个数和第 r 个数,所在的位置在同一行,那么对于其之间的和,直接利用等差数列求和公式即可。另一种一般情况,如果不在同一行的话,考虑将其分成三部分求和。

第①部分求和:数列中第 l 个数所在的行:计算该数(该数其实就是数列中第 l 个数所在的列)到该行最后一个数的和

第②部分求和:数列中第 l 个数所在的行到第 r 个数所在的行(不包含 l 和 r ):每一行都是完整的,(整体结构类似一个梯形矩阵),所以也采用等差数列求和公式即可。

第③部分求和:数列中第 r 个数所在的行:计算从1开始到该数(该数其实就是数列中第 r 个数所在的列)的和。

最后将这三部分的和相加即可。
 

C++参考程序代码如下:

#include <iostream>
#include <cmath>
using namespace std;
struct pos {  //表示第x行,第y列的数的位置
	long long x,y;
};
pos get(long long n) {  //获取数列的第n项所在的位置(行,列)
	long long t=(sqrt(1+8*n)-1)/2;
	pos p;
	if(t*(t+1)/2==n) {
		p.x=t;
		p.y=t;
	} else {
		p.x=t+1;
		p.y=n-t*(t+1)/2;
	}
	return p;
}
long long presum(long long n) {  //求数列的前n行的各列所有元素的和
	return (n*(n+1)*(2*n+1)/6+n*(n+1)/2)/2;
}
int main() {
	int T;
	cin>>T;
	while(T--) {
		long long l,r,ans;
		cin>>l>>r;
		long long x1=get(l).x, y1=get(l).y;  //获取数列中第l个数的位置(行,列)
		long long x2=get(r).x, y2=get(r).y;  //获取数列中第r个数的位置(行,列)
		if(x1==x2) {  //如果数列中的第l个数和第r个数在同一行
			ans=(y1+y2)*(y2-y1+1)/2;
		} else {  //不在同一行,将其中间分成三部分求和
			long long sum1=(y1+x1)*(x1-y1+1)/2;  //在第l个数所在的行(第x1行),计算从y1到该行最后一个数(x1)的和
			long long r=x2-x1-1,sum2;
			sum2=r*(1+x1)*x1/2+r*(1+r)/2*x1+presum(r);  //计算x1+1行到x2-1行之间,所有元素的和
			long long sum3=(1+y2)*y2/2;   //在第r个数所在的行(第x2行),计算1开始到y2的和
			ans=sum1+sum2+sum3;
		}
		cout<<ans<<endl;
	}
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值