题目描述
小蓝发现了一个有趣的数列,这个数列的前几项如下:
1, 1, 2, 1, 2, 3, 1, 2, 3, 4, \cdots1,1,2,1,2,3,1,2,3,4,⋯
小蓝发现,这个数列前 11 项是整数 11,接下来 22 项是整数 11 至 22,接下来 33 项是整数 11 至 33,接下来 44 项是整数 11 至 4,依次类推。
小蓝想知道,这个数列中,连续一段的和是多少。
输入描述
输入的第一行包含一个整数 TT,表示询问的个数。
接下来 TT 行,每行包含一组询问,其中第 ii 行包含两个整数 l_ili 和 r_iri,表示询问数列中第 l_ili 个数到第 r_iri 个数的和。
输出描述
输出 TT 行,每行包含一个整数表示对应询问的答案。
输入输出样例
示例
输入
3
1 1
1 3
5 8
输出
1
4
8
评测用例规模与约定
对于 1010% 的评测用例,1 ≤ T ≤ 30, 1 ≤ l_i ≤ r_i ≤ 1001≤T≤30,1≤li≤ri≤100。
对于 2020% 的评测用例,1 ≤ T ≤ 100, 1 ≤ l_i ≤ r_i ≤ 10001≤T≤100,1≤li≤ri≤1000。
对于 4040% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ l_i ≤ r_i ≤ 10^61≤T≤1000,1≤li≤ri≤106。
对于 7070% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ l_i ≤ r_i ≤ 10^91≤T≤10000,1≤li≤ri≤109。
对于 8080% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ l_i ≤ r_i ≤ 10^{12}1≤T≤1000,1≤li≤ri≤1012。
对于 9090% 的评测用例,1 ≤ T ≤ 10000,1 ≤ l_i ≤ r_i ≤10^{12}1≤T≤10000,1≤li≤ri≤1012。
对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ l_i ≤ r_i ≤ 10^{12}1≤T≤100000,1≤li≤ri≤1012。
运行限制
最大运行时间:5s
最大运行内存: 256M
//二分法
//思路:核心在于,我们可以通过迭代求出每一个小区间的和
//以及前n个区间的所有和(题设总会有上限数字,通过上限来先存好已知数据)
//再用二分找出i具体在区间的位置就可以得出答案.
#include <stdio.h>
#include <stdlib.h>
#define max 1414214//不用加;
//区间下标上限
//题设是第l个数到r个数的和
//最大上限是10的12次方个数字
//跟据观察发现每组都是等差数列
//是指每一个区间的数字数量
//1+2+3+....+n
//计算可知最多为1414214组
//Sn=1e12
//n=1414214
//用二分法的前提一定是要明确查找的左右边界才能查找的!
long long a[max];
long long s[max];
//二分查找
//用该方法查找出i前一个或者其中的数列下标(a[mid]),然后带入公式
//注意,这里是对a数组的下标查找
//这个mid是查找数组的下标
long long sum(long long i)
//i是当前位置下标
{
long long l=0,r=max;//两个数组边界下标
//开始l为0是因为这是数组下标的边界
while(l<r)//现在看回来就是求极限和边界问题,l、r的形式
{
long long mid=(l+r+1)/2;
//+1是因为l不动
//核心是寻找a[j]<=i的情况
//至于为什么是等于这是后来的
//因为相等时公式同样成立就归结在一起了
//不是a[j]<=i是事实,而是我要找a[j]<=i的情况
if(a[mid]>i)//不可大于i,不满足条件往左边缩,并且不取mid值
r=mid-1;
else//满足<=i,可取mid值,并且继续往右边找
l=mid;
//一直到l==r时得到极限边界
//该查找一直向右找,
//核心就是为了得出i个数的前一个小区间的下标是多少
//然后带入公式,这个公式也很容易知道的
//前面的一堆+当前小区间所在数之前的所有数之和
}
//就是一直压缩位置的范围,然后就可以快速输出(因为数据不大)
//锁定边界和极限这个事情循环还是很难办到的
//二分法最好处理
//传i校对l、r
return s[r]+a[i-a[r]];
//这个是递推式,易知
//想一想,其实很显然,每一个小区间之和都是等于在这个区间里最大的数的位置的
//因此a[r]就是第r个数的位置,也就是在i的前一个区间的最后一个数的位置
//所以i-a[r]=i-位置第r的数,就可以得到当前小区间所在数之前的所有数之和
//r和l都可以
}
int main(int argc, char *argv[])
{
for(int i=1;i<=max;i++)//迭代
{
a[i]=a[i-1]+i;//每一个小区间的元素之和
s[i]=s[i-1]+a[i];//前i个所有区间的元素之和
}
//这是直接写出预设条件的总量,把数据范围内的所有数据准备好
//因为插入数据并不会浪费多少时间
int n;
long long l,r;
scanf("%d",&n);
while(n--)
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",sum(r)-sum(l-1));
//关于函数的,就看你函数返回的值是什么类型的
//这里l-1是因为l的数字也要包括在内,所以往前挪
//再验证结果的时候一定要自己带入一些简单的数据
//最起码的样例数据输入看看是否正确,来判定逻辑是否出错
}
return 0;
}


总结:使用二分法,并且确定好二分的对象
寻找数列之间的规律,寻找不限于题设所给的新变量