一 问题描述
马里奥是世界著名的水管工。他的“魁梧”形象和惊人的跳跃能力深深地留在在我们的记忆中。现在这位可怜的公主再次陷入困境,马里奥需要拯救他的情人。我们把通往老板城堡的道路视为一条线(长度为n) ,在每个整数点 i 上都有一块高度位 hi 的砖。现在的问题是,如果他能跳的最大高度是 H ,那么 Mario 在区间[L,R]中可以跳过多少砖块。
二 输入和输出
1 输入
第一行是整数 T,即测试数据的数量。
对于每个测试数据:
第一行包含两个整数 n,m,n 是 道路的长度,m 是查询的数量。
下一行包含 n 个整数,每个转的高度。
接下来的 m 行,每行包含三个整数 L,R,H
2 输出
对于每种情况,每行输出“Case X:”(X 是从 1 开始的案例编号),后跟 m 行,每行包含一个整数。第 i 个整数是 Mario 可以为第 i 个查询命中的转块数。
三 输入和输出样例
1 输入样例
1
10 10
0 5 2 7 5 4 3 8 7 7
2 8 6
3 5 0
1 3 1
1 9 4
0 1 0
3 5 5
5 5 1
4 6 3
1 5 7
5 7 3
2 输出样例
Case 1:
4
0
0
3
1
2
0
1
5
1
四 分析和设计
1 分析
本问题包括区间查询。区间查询比较特殊,查询小于等于 H 的数有多少个。
可以采用分块的方法解决。
2 设计
a 分块
划分块,每块非递减排序。
b 查询
查询区间[l,r]有多少个数小于等于 h。
如果该区间属于同一块,则暴力累加块内有多少个数小于等于 h。
如果该区间包含多块,则累加中间每一块小于等于 h 的数,然后暴力累加左端和右端有多少个数小于等于h。
lower_bound()和upper bound()都是利用二分查找的方法在一个排好序的数组中进行查找的。需要引入头文件:algorithm.
五 代码
package com.platform.modules.alg.alglib.hdu4417;
import java.util.Arrays;
import static java.lang.Math.sqrt;
public class Hdu4417 {
public final int maxn = 100000;
int L[] = new int[maxn];
int R[] = new int[maxn];
int belong[] = new int[maxn];
int a[] = new int[maxn];
int temp[] = new int[maxn];
int n;
int m;
public String output = "";
void build() {
int t = (int) sqrt(n);
int num = n / t;
if (n % num != 0) num++;
for (int i = 1; i <= num; i++) {
L[i] = (i - 1) * t + 1;
R[i] = i * t;
}
R[num] = n;
for (int i = 1; i <= n; i++)
belong[i] = (i - 1) / t + 1;
for (int i = 1; i <= num; i++) {
// 每块排序
Arrays.sort(temp, L[i], 1 + R[i]);
}
}
int query(int l, int r, int h) {
int ans = 0;
if (belong[l] == belong[r]) {
for (int i = l; i <= r; i++)
if (a[i] <= h) ans++;
} else {
for (int i = l; i <= R[belong[l]]; i++)//左端
if (a[i] <= h) ans++;
for (int i = belong[l] + 1; i < belong[r]; i++)//中间
ans += upperBound(temp, L[i], R[i] + 1, h) - L[i];
for (int i = L[belong[r]]; i <= r; i++)//右端
if (a[i] <= h) ans++;
}
return ans;
}
/**
* @param arr 数组
* @param value 待比较的值
* @param left 左端点下标
* @param right 右端点下标
* @return 第一个大于value的数的坐标
*/
int upperBound(int[] arr, int left, int right, int value) {
int l = left, r = right - 1;
while (l <= r) {
int m = (l + r) / 2;
if (arr[m] <= value) {
l = m + 1;
} else { // arr[m] > value
r = m - 1;
}
}
return l;
}
public String cal(String input) {
String[] line = input.split("\n");
String[] num = line[0].split(" ");
n = Integer.parseInt(num[0]);
m = Integer.parseInt(num[1]);
String[] keys = line[1].split(" ");
for (int i = 1; i <= n; i++) {
a[i] = Integer.parseInt(keys[i - 1]);
temp[i] = a[i];
}
build();
int j = 0;
while (m-- > 0) {
int l, r, h;
String[] command = line[2 + j].split(" ");
j++;
l = Integer.parseInt(command[0]);
r = Integer.parseInt(command[1]);
h = Integer.parseInt(command[2]);
output += query(++l, ++r, h) + "\n";
}
return output;
}
}
六 测试