HDU 3530 Subsequence 尺取 RMQ

题意: 给出一个数组, 长度为1e5, 1次询问, 给出一个上界R, 一个下界L, 问最大值减最小值差∈[L, R]的区间最长是多少.

思路: RMQ当然可以维护M-m, 但是因为对M-m有两个限制, 没有单调性就不能二分解决..只能用我不太熟悉的尺取了.

尺取的具体思路见代码:

        // a~b如果M-m>上界, a~c只会增大M, 减小m
        // 增大M减小m后, 更不可能合法.
        // 所以只要当前区间M-m>上界, 不需要再考虑左端点不动的其他区间

        // 1~n枚举右端点的位置i, 表示在已经考虑右端点<i的所有区间.
        // 如果当前区间M-m>上界, 左端点pos+=1(此后的合法区间左端点不可能<=pos)

        // 检查当前区间合法, 更新ans
        // 右端点更新i++
        for (int i = 1; i <= n; i++) {
            int tmp = RMQ::RMQ_max(pos, i) - RMQ::RMQ_min(pos, i);
            if (tmp > k)pos++;

            tmp = RMQ::RMQ_max(pos, i) - RMQ::RMQ_min(pos, i);

            if (tmp <= k && tmp >= m) {
                ans = max(ans, i - pos + 1);
            }
        }
#include<bits/stdc++.h>
using namespace std;
const int M = 2e5 + 5;
namespace RMQ {
//可以从0也可以从 1开始,30应该能使用 1e6以内的

    const int MX = M;
    const int P = 17;
    int MIN[MX][P], MAX[MX][P];
    int lg[MX];
    int A[MX];

    void RMQ_init1() {//预处理使RMQ的复杂度变为O(1)
        for (int i = 1, j = 0; i < MX; ++i) {
            if (i <= (1 << (j + 1)))
                lg[i] = j;
            else
                lg[i] = ++j;
        }
    }

    void RMQ_init2(int n) {
        for (int i = 0; i < n + 1; ++i) {
            MAX[i][0] = MIN[i][0] = A[i];
        }
        for (int j = 1; (1 << j) <= n + 1; ++j) {
            for (int i = 0; i + (1 << j) - 1 < n + 1; ++i) {
                MAX[i][j] = max(MAX[i][j - 1], MAX[i + (1 << (j - 1))][j - 1]);
                MIN[i][j] = min(MIN[i][j - 1], MIN[i + (1 << (j - 1))][j - 1]);
            }
        }
    }

    //要保证L<=R
    int RMQ_min(int L, int R) {
        int k = lg[R - L + 1];
        //int k =0;while((1<<(k+1))<R-L+1) k++;//用这个的话, 就不需要先算log了
        return min(MIN[L][k], MIN[R - (1 << k) + 1][k]);
    }

    int RMQ_max(int L, int R) {
        int k = lg[R - L + 1];
//int k =0;while((1<<(k+1))<R-L+1) k++;//用这个的话, 就不需要先算log了
        return max(MAX[L][k], MAX[R - (1 << k) + 1][k]);
    }
}

int n, m, k;

bool jug(int len) {
    for (int s = 1; s + len - 1 <= n; s++) {
        int t = s + len - 1;
        int tmp = RMQ::RMQ_max(s, t) - RMQ::RMQ_min(s, t);
        if (tmp >= m) {
            return true;
        }
    }
    return false;
}

bool jug2(int len) {
    for (int s = 1; s + len - 1 <= n; s++) {
        int t = s + len - 1;
        int tmp = RMQ::RMQ_max(s, t) - RMQ::RMQ_min(s, t);
        if (tmp <= k) {
            return true;
        }
    }
    return false;
}
int ans;
inline int read() {
    int ret = 0, c, f = 1;
    for (c = getchar(); !(isdigit(c) || c == '-'); c = getchar());
    if (c == '-') f = -1, c = getchar();
    for (; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
    if (f < 0) ret = -ret;
    return ret;
}

void init() {
    RMQ::RMQ_init1();
    while (~scanf("%d%d%d", &n, &m, &k)) {
        ans = 0;
        for (int i = 1; i <= n; i++)RMQ::A[i] = read();
        RMQ::RMQ_init2(n);
        int pos = 1;
        // a~b如果M-m>上界, a~c只会增大M, 减小m
        // 增大M减小m后, 更不可能合法.
        // 所以只要当前区间M-m>上界, 不需要再考虑左端点不动的其他区间

        // 1~n枚举右端点的位置i, 表示在已经考虑右端点<i的所有区间.
        // 如果当前区间M-m>上界, 左端点pos+=1(此后的合法区间左端点不可能<=pos)

        // 检查当前区间合法, 更新ans
        // 右端点更新i++
        for (int i = 1; i <= n; i++) {
            int tmp = RMQ::RMQ_max(pos, i) - RMQ::RMQ_min(pos, i);
            if (tmp > k)pos++;

            tmp = RMQ::RMQ_max(pos, i) - RMQ::RMQ_min(pos, i);

            if (tmp <= k && tmp >= m) {
                ans = max(ans, i - pos + 1);
            }
        }
        printf("%d\n", ans);
    }
}

int main() {
    init();
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值