题目
假定有一个无限长的数轴,数轴上每个坐标都是0.
现在,我们首先进行n次操作,每次操作将某一位置x上的数加c。
然后,进行 m 次询问,每个询问包含两个整数l和r,你需要求出在区间[l, r]之间的所有数的和。
输入格式
第一行包含两个整数 n 和 m;
接下来的n行,每行包含两个整数 x 和 c;
再接下来的 m 行,每行包含两个整数 l 和 r。
输出格式
共m行,每行输出一个询问中所求的区间内数字和。
数据范围
−109 ≤ x ≤ 109 ,
1 ≤ n,m ≤ 105,
−109≤ l ≤ r ≤ 109,
−10000 ≤ c ≤ 10000
输入样例
3 3 1 2 3 6 7 5 1 3 4 6 7 8 \begin{matrix} 3 & 3 \\ 1 & 2 \\ 3 & 6 \\ 7 & 5 \\ 1 & 3 \\ 4 & 6 \\ 7 & 8 \end{matrix} 31371473265368
输出样例
8 0 5 \begin{matrix} 8 \\ 0 \\ 5 \end{matrix} 805
思路
因为数轴上的范围很大,而需要用到的坐标数量很少,直接开对应数量级范围的数组显然不可取;离散化即为映射,将使用到的不连续的数字坐标映射到连续的数组的下标,可以减少空间的需求和计算量;
伪代码步骤
1,因为要用到数轴上的坐标,则将所有用到的坐标全部放在数组alls中;
2,对数组中的元素进行排序、去重,得到数组a;
3,n次操作中,每次操作将某一位置x上的数加上c;加到数组sum中;
4,根据sum数组求其前缀和数组s;
5,m次操作,每次操作是求区间和;即通过前缀和数组s进行求解即可;
注意:下标转换,边界条件
代码
import java.util.*;
public class Main{
static int n;
static int m;
static int[][] arr; //相加数组
static int[][] interval; //区间数组
static int[] sum;
static int[] s; //前缀和数组
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
//alls数组用来存储输入数据的所有使用的坐标
List<Integer> alls = new ArrayList<>();
arr = new int[n][2];
for(int i = 0; i < n; i++){
for(int j = 0; j < 2; j++){
arr[i][j] = sc.nextInt();
alls.add(arr[i][0];
}
}
interval = new int[m][2];
for(int i = 0; i < m; i++){
for(int j = 0; j < 2; j++){
interval[i][j] = sc.nextInt();
alls.add(interval[i][j];
}
}
//alls数组排序
Collections.sort(alls); //Collections.sort()用于集合排序
//通过去重函数unique()进行去重
int[] a = unique(alls);
//根据给定的数据 ,将数轴上的某一位置x上的数加c
sum = new int[a.length];
for(int i = 0; i < arr.length; i++){
//调用find()函数,求在排序去重数组映射后的新坐标值
int index = find(arr[i][0], a);
sum[index - 1] += arr[i][1];
}
s = new int[sum.length + 1]; //前缀和数组
s[0] = 0;
//求前缀和数组
for(int i = 1; i < s.length; i++){
s[i] = s[i - 1] + sum[i - 1];
}
//计算输出区间和
for(int i = 0; i < interval.length; i++){
int l = find(interval[i][0], a);
int r = find(interval[i][1], a);
//用前缀和数组来计算区间和(第l个到第r个之间的和)
System.out.println(s[r] - s[l - 1]);
}
}
//去重函数---因为给定的数据中会有重复,需要去重再映射才能得到新坐标
public static int[] unique(ArrayList<Integer> list){
List<Integer> tmp= new ArrayList<>();
tmp.add(list.get(0);
int i = 0, j = 1;
while(j < list.size()){
if(list.get(j) != list.get(i)){
i = j;
tmp.add(list.get(i);
}
j++;
}
//将结果存到数组中
int[] ans = new int[tmp.size()];
for(int k = 0; k < tmp.size(); k++){
ans[k] = tmp.get(k);
}
return ans;
}
//find()函数用来找指定坐标映射后的新下标
//因为a数组中是排序去重后的原坐标,所以可以用二分法模板实现
public static int find(int x, int[] a){
int l = 0, r = a.length - 1;
while(l < r){
int mid = l + r >> 1;
if(a[mid] >= x){
r = mid;
}
else{
l = mid + 1;
}
}
return l + 1; //加1是为了前缀和数组对应的区间和好计算
}
}