牛客多校第五场 K King of Range

题意:

给定一个\(n\)个数得序列\(a_i\),给定\(m\)个询问,每次给出一个\(k\),寻找有多少个区间\([l, r]\)中最大值与最小值之差严格大于\(k\)

思路:

可以发现,如果已经知道一个区间最大值与最小值严格大于k之后,那么我们便可以往从两头这个区间随意加数并且会对答案有贡献:
如果加一个比最大值大的或者比最小值小的数,那么这个区间差值还是严格大于\(k\)
如果加一个介于最大值最小值之间的数,那么这个区间差值不会发生变化。
那么发现这一点之后便可以使用双指针寻找区间了,那么怎么维护区间最值呢(比赛的时候想到了用双指针但没想到怎么维护最值),\(ST\)表就派上用场了,那么我们固定一个\(i\),让\(j\)去寻找即可,找到第一个合适的区间就可以停下了,那么j往后的所有数都可以加到这个区间中而对答案产生贡献就是\(n - j + 1\)

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1E5 + 10, M = 18;

int n, m, k;
int w[N];
int f1[N][M], f2[N][M];
int lg[N];

void init() {
    for (int j = 0; j < M; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            if (!j) f1[i][j] = w[i], f2[i][j] = w[i];
            else {
                f1[i][j] = max(f1[i][j - 1], f1[i + (1 << j - 1)][j - 1]);
                f2[i][j] = min(f2[i][j - 1], f2[i + (1 << j - 1)][j - 1]);
            }
        }
    }
}

bool check(int l, int r, int k) {
    int len = r - l + 1;
    int d = lg[len];
    int maxD = max(f1[l][d], f1[r - (1 << d) + 1][d]);
    int minD = min(f2[l][d], f2[r - (1 << d) + 1][d]);
    return maxD - minD > k;
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
    
    lg[2] = 1;
    for(int i=3;i<=n;i++) {
        lg[i] = lg[i / 2] + 1;
    }
    
    init();

    while (m--) {
        long long res = 0;
        int k; scanf("%d", &k);
        for (int l = 1, r = 1; l <= n; l++) {
            while (!check(l, r, k) && r <= n && l <= r) r++;
            if (r <= n) res += n - r + 1;
        }
        printf("%lld\n", res);
    }

    return 0;
}

值得一提的是,用库函数\(log\)函数的效率有点不行,会被卡,所以还是乖乖预处理一个\(log\)数组吧。

posted @ 2021-08-01 17:57  Xxaj5  阅读(41)  评论(0编辑  收藏  举报