牛客多校2021(五)K.King of Range(ST表、双指针)

  • 题目:King of Range

  • 题意:给出一个序列,问其存在多少个子序列(子序列肯定连续~)满足子序列中最大值与最小值的差大于k。

  • 思路:ST表预处理 + 双指针维护区间。

  • 解析:以下用\(maxv, minv\)表示该区间最大值和最小值,假设当遍历到区间[l, r - 1]时,\(maxv - minv \leq k\)​说明该区间内不可能存在子区间满足条件,但当遍历到[l, r]区间时, \(maxv - minv > k\)​​ 说明[l, r]区间满足条件, 并且[l, r + 1],[l, r + 2]...[l, n]一定都满足条件。

    那么我们可以首先用ST表预处理出所有区间的\(maxv, minv\),然后利用双指针\(i\)为头指针,\(j\)为尾指针,如果当前区间\(maxv - minv \leq k\),那么尾指针\(j\)​​需要往后移动(有一些细节附加条件可以看代码),否则需要累加答案\(n - j + 1\)并且头指针\(i\)需要往后移动。

    细节:此题在双指针维护区间过程中需要多次查询区间最大值和最小值,若log2()函数是直接用函数库的则会T掉,log2()函数时间复杂度为log(n),所以需要预处理\(log_2(1)\)​​~\(log_2(n)\)​​

  • 代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    const int M = 20;
    
    int a[N], f[N][M], g[N][M]; //f(i, j)表示[i, i + 2^j - 1]最小值, g(i, j)表示最大值
    int lg[N]; //log2(x)
    int n, m, k;
    
    void fun() //预处理log2()函数
    {
        lg[0] = -1;
        for(int i = 1; i <= n; i++) lg[i] = lg[i >> 1] + 1;
    }
    
    void st_init()
    {
        for(int i = 1; i <= n; i++)
        {
            f[i][0] = a[i];
            g[i][0] = a[i];
        }
        for(int j = 1; j <= log2(n); j++)
        {
            for(int i = 1; i <= n + 1 - (1 << j); i++)
            {
                f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
                g[i][j] = max(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    
    int query_min(int l, int r)
    {
        int len = r - l + 1;
        int k = lg[len];
        return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
    
    int query_max(int l, int r)
    {
        int len = r - l + 1;
        int k = lg[len];
        return max(g[l][k], g[r - (1 << k) + 1][k]);
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        st_init();
        fun();
        while(m --)
        {
            scanf("%d", &k);
            ll num = 0;
            for(int i = 1, j = 1; i <= n; i++)
            {
                while( (i >= j || query_max(i, j) - query_min(i, j) <= k) && (j < n) )
                    j ++;
                if(query_max(i, j) - query_min(i, j) > k) //题目条件
                    num += ll(n - j + 1);
            }
            printf("%lld\n", num);
        }
        return 0;
    }
    
    
上一篇:King of Range


下一篇:2021牛客暑期多校训练营5 K. King of Range(单调队列)详细题解