超级马里奥

一 问题描述

马里奥是世界著名的水管工。他的“魁梧”形象和惊人的跳跃能力深深地留在在我们的记忆中。现在这位可怜的公主再次陷入困境,马里奥需要拯救他的情人。我们把通往老板城堡的道路视为一条线(长度为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;
    }
}

六 测试

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值