123(详细解析)

题目描述

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

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≤liri≤100。

对于 2020% 的评测用例,1 ≤ T ≤ 100, 1 ≤ l_i ≤ r_i ≤ 10001≤T≤100,1≤liri≤1000。

对于 4040% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ l_i ≤ r_i ≤ 10^61≤T≤1000,1≤liri≤106

对于 7070% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ l_i ≤ r_i ≤ 10^91≤T≤10000,1≤liri≤109

对于 8080% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ l_i ≤ r_i ≤ 10^{12}1≤T≤1000,1≤liri≤1012

对于 9090% 的评测用例,1 ≤ T ≤ 10000,1 ≤ l_i ≤ r_i ≤10^{12}1≤T≤10000,1≤liri≤1012

对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ l_i ≤ r_i ≤ 10^{12}1≤T≤100000,1≤liri≤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;
}

总结:使用二分法,并且确定好二分的对象

  1. 寻找数列之间的规律,寻找不限于题设所给的新变量

评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值