2021牛客暑期多校训练营5-K.King of Range(尺取法&倍增&线段树)

收获&思考&感受

  1. 又被这题搞崩 了,怎么就没想着换一种方法?能静下心来思考的话很容易发现线段树是不行的,复杂度太高(即使有我所谓的离线优化,也是可能达到 O ( 1 e 7 ∗ l o g ( 1 e 7 ) ) O(1e7*log(1e7)) O(1e7log(1e7)的emmm。
  2. 倍增也还是不够熟练,当作是练习吧。
  3. 还好是自己一个人玩,不然怕会被队友骂死。16wa都没搞出来,我可真牛逼(主要是没换思路emm),开始使用的set,后面换成线段树(后来发现复杂度差不多emm)
  4. 而且这一题后面用正确代码试了试,m的最大值应该是200(就,不当回事儿吧,题目的锅,不是我的锅hh?怎么可能没有锅,怎么就像之前一样,就认为自己的思路是对的,就是不知道改变思路?

ST表

模板

int Log[N], Bit[N];
int fmi[N][25], fmx[N][25];
// 初始化:::
void init() {
    Bit[0] = 1, Log[1] = 0;
    for (int i = 1; i <= 20; i++) Bit[i] = Bit[i - 1] * 2;
    for (int i = 2; i <= n; i++) Log[i] = Log[i / 2] + 1;
}
// 查询区间[l,r]的最大值&最小值&极差
bool ok(int l, int r, int k) {
    int s = Log[r - l + 1];
    //这里不超出访问区间就ok了
    int mx = max(fmx[l][s], fmx[r - Bit[s] + 1][s]);//返回区间最大值
    int mi = min(fmi[l][s], fmi[r - Bit[s] + 1][s]);//返回区间最小值
    return (mx - mi) <= k;
}
signed main {
    init();
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        fmi[i][0] = fmx[i][0] = a[i];
    }
    for (int j = 1; j <= 20; j++) {
        //这个i+Bit[j]-1也很精髓,fxx[i][j]不能表示的区间就不要查询
        for (int i = 1; i + Bit[j] - 1 <= n; i++) {
            //拆成两个区间
            fmi[i][j] = min(fmi[i][j - 1], fmi[i + Bit[j - 1]][j - 1]);
            fmx[i][j] = max(fmx[i][j - 1], fmx[i + Bit[j - 1]][j - 1]);
        }
    }
}

题目

  1. 传送门
  2. 题意
    2.1 给一个长度为n的数组a,和m次查询k,输出m行,每行一个整数,表示第i次查询时所有满足“区间最大值-区间最小值>k”的区间个数。
    2.2 区间: 1 ≤ l i ≤ r i ≤ n 1\le l_i\le r_i \le n 1lirin
    2.3 1 ≤ n ≤ 1 e 5 , 1 ≤ m ≤ 100 , 1 ≤ a i ≤ 1 e 9 , 1 ≤ k ≤ 1 e 9 1\le n\le 1e5,1\le m\le 100,1\le a_i\le 1e9,1\le k\le 1e9 1n1e5,1m100,1ai1e9,1k1e9
  3. 题解
    3.1 set,线段树维护区间最大值以及最小值都会超时!!!!
    3.2 提示:应该用ST表(时间复杂度一会再来研究)(单词查询时间复杂度为 O ( 1 ) O(1) O(1)
    3.3 具体的见上面模板,怎么 O ( 1 ) O(1) O(1)查询区间最大值&区间最小值
  4. 代码
#include <bits/stdc++.h>
// #define int long long
#define ll long long
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 1e5 + 10;
int n, m, a[N], k;
int Log[N], Bit[N];
int fmi[N][25], fmx[N][25];
void init() {
    Bit[0] = 1, Log[1] = 0;
    for (int i = 1; i <= 20; i++) Bit[i] = Bit[i - 1] * 2;
    for (int i = 2; i <= n; i++) Log[i] = Log[i / 2] + 1;
    /*
    i Log[i]
    1 0
    2 1
    3 1
    4 2
    5 2
    6 2
    7 2
    8 3
    9 3
    10 3
    11 3
    12 3
    13 3
    14 3
    15 3
    16 4
    17 4
    18 4
    19 4
    20 4
    21 4
    22 4
    23 4
    24 4
    25 4
    26 4
    27 4
    28 4
    29 4
    30 4
    */
    //一般来说,不超出访问区间就ok了
}
bool ok(int l, int r, int k) {
    int s = Log[r - l + 1];
    //这里不超出访问区间就ok了
    int mx = max(fmx[l][s], fmx[r - Bit[s] + 1][s]);
    int mi = min(fmi[l][s], fmi[r - Bit[s] + 1][s]);
    return (mx - mi) <= k;
}
signed main() {
    cin >> n >> m;
    init();
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        fmi[i][0] = fmx[i][0] = a[i];
    }
    for (int j = 1; j <= 20; j++) {
        //这个i+Bit[j]-1也很精髓
        for (int i = 1; i + Bit[j] - 1 <= n; i++) {
            //拆成两个区间
            fmi[i][j] = min(fmi[i][j - 1], fmi[i + Bit[j - 1]][j - 1]);
            fmx[i][j] = max(fmx[i][j - 1], fmx[i + Bit[j - 1]][j - 1]);
        }
    }
    /*
        for (int i = 1; i <= n; i++) {
            dbg(i);
            for (int j = 0; j <= 20 && i + Bit[j] - 1 <= n; j++) {
                // cout << fmi[i][j] << " ";
                // cout << fmx[i][j] << " ";
            }
            cout << "\n";
        }
      */
    ll sum = 1ll * n * (n + 1) / 2, ans;
    while (m--) {
        scanf("%d", &k);
        ans = sum;
        int r = 0;
        for (int i = 1; i <= n; i++) {
            //查询区间极差复杂度达到O(1)
            while (r < n && ok(i, r + 1, k)) ++r;
            // cout << i << " " << r << endl;
            ans -= 1ll * (r - i + 1);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值