题目描述
问题描述
给出一个n长的数列,再进行m次询问,每次询问询问两个区间[L1,R1],[L2,R2],
询问数列第L2到R2个数字每一个数在数列第L1到R1个数中有多少个数字不大于它。
其中 n, m ≤ \le ≤ 1000
题解
如果是暴力做法的话,时间复杂度是 O ( n 2 ∗ m ) O(n^2 * m) O(n2∗m)
就是对于每一个query中的数字遍历一遍 [ l 1 , r 1 ] [l1, r1] [l1,r1]。但是最后一步可以优化,就是可以在 O ( l o g n ) O(logn) O(logn)的时间复杂度内解决数字个数问题。
解决的办法是什么呢?树状数组or线段树
- 直接把 [ l 1 , r 1 ] [l1, r1] [l1,r1]中的数组数字a[j]放入到树状数组中进行计数。然后对每一个 [ l 2 , r 2 ] [l2, r2] [l2,r2]中的a[j]求个数就行了。
- java代码, 时间复杂度 O ( m n l o g ( m a x v ) ) O(mnlog(maxv)) O(mnlog(maxv))其中 m a x v = m a x ( a ) maxv = max(a) maxv=max(a)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
/**
* @author: Zekun Fu
* @date: 2022/10/25 23:21
* @Description: 蓝桥杯算法训练比较
* 1000 * 1000 * (l1, r1)中小于x的数字个数是有多少,这个可以O(logn)进行解决。解决的办法是什么呢?树状数组or线段树
* 直接把[l1, r1]中的数组放入到树状数组中进行计数。然后对每一个[l2, r2]中的a[j]求个数就行了。
*
*/
public class Main{
private static int maxn = 1005;
private static int[] c = new int[maxn];
private static int[] a = new int[maxn];
private static int n, m;
private static Scanner sc = new Scanner(System.in);
private static int lowbit(int x) {
return x & -x;
}
private static void add(int x, int num) {
for (int i = x; i < maxn; i += lowbit(i)) {
c[i] += 1;
}
}
private static int sum(int x) {
int res = 0;
for (int i = x; i != 0; i -= lowbit(i)) {
res += c[i];
}
return res;
}
public static void main(String[] args) {
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt() + 1;
}
for (int i = 0; i < m; i++) {
int a1 = sc.nextInt();
int b1 = sc.nextInt();
int a2 = sc.nextInt();
int b2 = sc.nextInt();
Arrays.fill(c, 0);
for (int j = a1 - 1; j < b1; j++) {
add(a[j], 1);
}
for (int j = a2 - 1; j < b2; j++) {
int x = a[j];
if (j != b2 - 1)
System.out.print(sum(x) + " ");
else System.out.println(sum(x));
}
}
}
}