123 2021年国赛 二分搜索+前缀和

文章描述了一种有趣的数列,其特点是连续的项包含从1到当前项数的所有整数。为了高效地计算数列中某段连续项的和,可以使用前缀和配合二分查找的方法。通过预先计算每个区间(1,2,...,i)的前缀和,然后在查询时快速定位到给定数值所在的区间,从而得出答案。文章提供了具体的Java代码实现,展示了如何应用这种策略来解决此问题。
摘要由CSDN通过智能技术生成

题目描述

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

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 行,每行包含一个整数表示对应询问的答案。

输入输出样例

示例

输入

3
1 1
1 3
5 8

输出

1
4
8

 

运行限制

  • 最大运行时间:5s
  • 最大运行内存: 256M

 思路:前缀和+二分查找

主要是考察前缀和,我们将数组划分为若干个小区间,第i个区间是1,2,3....,i。拿到一个数后,需要快速的确定这个数属于哪个区间的。这就可以通过前缀和来记录,每个区间的最后一个元素的索引。然后通过二分查找来快速确定是在哪个区间,并确定大致的索引下标。最终的索引下标=前一个区间的索引+数num所在区间的距离第一个元素的偏移量。

为此,我们还用另个前缀和数组,计算i个区间之前的元素之和qz[a]。

根据用例规模粗略估计,数组需要开到10^{7}长度,3^{7}的int类型数组大约为128mb的内存;3^{7}的long数组大约为256mb。本例中用例2^{7}长度的long数组,实测内存在188mb左右。

AC代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;


public class Main {

    private static long[] qz_interval;
    private static long[] qz;

    public static void main(String[] args) throws IOException{
        StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        in.nextToken();
        int N = (int)in.nval;
        long max = (int) 9E6;
        qz_interval = new long[10000001];
        qz = new long[10000001];
        //计算可能出现的最大区间数为10E6的前缀和
        for (int i = 1; i < max; i++) {
            //第i个区间有i个元素
            //第i区间的任意一个数前面至少qz_interval[i-1]个元素
            //第i区间的最后一个元素是数组第qz_interval[i]个元素
            //qz_interval是用来二分查找数组中的第m个元素在第几个区间中
            qz_interval[i] = qz_interval[i-1] + i;
            //qz记录的是第i区间之前的前缀和
            //qz_interval[i]正好又是第i区间的元素之和  因为qz_interval[i] = 1+2+3+4+..+i
            //qz[i]就等于之前的前缀和加这个区间的和
            qz[i] = qz[i-1] + qz_interval[i];
        }
        //初始化完毕
        for (int i = 0; i < N; i++) {
            //N次询问
            in.nextToken();
            long a = (long) in.nval;
            in.nextToken();
            long b = (long) in.nval;
            System.out.println(qz(b)-qz(a-1));
        }

    }

    static long qz(long num){
        int a = Arrays.binarySearch(qz_interval,num);
        if(a<0){
            a = -a - 2;
        }
        return qz[a] + qz_interval[(int) (num - qz_interval[a])];
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值