严格次大值 一道数列分块入门题 题解

题目描述

给你一个长度为 \(n\) 的数列 \(a_1, a_2, \ldots, a_n\)

接下来有 \(q\) 次操作,操作分为如下两种类型:

  • \(\mathtt{1\ l\ r\ x}\):将下标在 \([l, r]\) 范围内的每个数(即 \(a_l, a_{l+1}, \ldots, a_r\))的数值各增加 \(x\)
  • \(\mathtt{2\ l\ r}\):查询下标在 \([l, r]\) 范围内的所有数值中的严格次大值,即查询 \(a_l, a_{l+1}, \ldots, a_r\) 中比 \(\max\limits_{l \le i \le r} \{ a_i \}\) 小的数里面最大的那个数的数值。特别地,如果不存在严格次大值(即 \(a_l = a_{l+1} = \ldots = a_r\)),输出 \(-1\)

你需要依次执行上述操作,并且对于每个查询操作,输出对应的结果。

输入格式

第一行,一个整数 \(n\),表示数列长度。

第二行,\(n\) 个整数 \(a_1, a_2, \ldots, a_n\),表示初始时数列中每个元素的数值。

第三行,一个整数 \(q\),表示操作次数。

接下来 \(q\) 行,每行包含一个操作(\(1\ l\ r\ x\)\(2\ l\ r\))。

输出格式

对于每次查询操作(\(2\ l\ r\)),输出一行,包含一个整数,表示数列 \(a\) 中第 \(l\) 个数到第 \(r\) 个数的严格次大值。

样例

样例输入1

5
1 2 3 4 5
6
2 2 3
1 1 2 1
2 2 3
1 3 5 2
1 1 2 7
2 1 5

样例输出1

2
-1
9

样例输入2

10
2 3 3 2 3 3 2 3 3 2
9
2 2 3
1 1 3 2
1 3 7 3
2 1 5
1 4 9 6
1 5 10 2
1 7 9 3
2 1 8
2 4 10

样例输出2

-1
6
14
14

说明/提示

数据规模与约定

  • 对于 \(50\%\) 的数据,\(n, q \le 1000\)
  • 对于 \(100\%\) 的数据,\(2 \le n \le 10^5, 1 \le q \le 10^5\)\(1 \le a_i, x \le 1000\)\(1 \le l \lt r \le n\)

题解

  • \(id_i\) 表示 \(a_i\) 所在的分块编号
  • \(d_i\) 表示第 \(i\) 个分块的整体增量;
  • \(m1_i\) 表示第 \(i\) 个分块的最大值(不考虑整体增量 \(d_i\)
  • \(m2_i\) 表示第 \(i\) 个分块的严格次大值(不考虑整理增量 \(d_i\)),如果这个分块没有严格次大值(即所有数都一样)则 \(m2_i = 0\)

然后你就会发现这是一道 数列分块 的入门模板题。

示例程序如下(代码有啥不懂的地方可以在下方留言):

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;

int n, blo, q, d[400], m1[400], m2[400], a[maxn], id[maxn];

void update(int p) {
    m1[p] = m2[p] = 0;
    for (int i = (p-1)*blo+1; i <= min(n, p*blo); i++)
        m1[p] = max(m1[p], a[i]);
    for (int i = (p-1)*blo+1; i <= min(n, p*blo); i++)
        if (a[i] != m1[p])
            m2[p] = max(m2[p], a[i]);
}

void add(int l, int r, int x) {
    if (id[l] + 1 >= id[r]) {
        for (int i = l; i <= r; i++)
            a[i] += x;
        update(id[l]);
        if (id[l] < id[r])
            update(id[r]);
    }
    else {
        for (int i = l; i <= id[l]*blo; i++)
            a[i] += x;
        update(id[l]);
        for (int i = (id[r]-1)*blo+1; i <= r; i++)
            a[i] += x;
        update(id[r]);
        for (int i = id[l]+1; i < id[r]; i++)
            d[i] += x;
    }
}

void f(int c, int &a, int &b) {
    if (c > a) {
        b = a;
        a = c;
    }
    else if (c < a && c > b)
        b = c;
}

int query(int l, int r) {
    int max1 = -1, max2 = -1;
    if (id[l] + 1 >= id[r]) {
        for (int i = l; i <= r; i++)
            f(a[i] + d[id[i]], max1, max2);
    }
    else {
        for (int i = l; i <= id[l]*blo; i++)
            f(a[i] + d[id[i]], max1, max2);
        for (int i = (id[r]-1)*blo+1; i <= r; i++)
            f(a[i] + d[id[i]], max1, max2);
        for (int i = id[l]+1; i < id[r]; i++) {
            f(m1[i] + d[i], max1, max2);
            if (m2[i])
                f(m2[i] + d[i], max1, max2);
        }
    }
    return max2;
}

int main() {
    scanf("%d", &n);
    blo = sqrt(n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a+i);
        id[i] = (i - 1) / blo + 1;
    }
    for (int i = 1; i <= id[n]; i++)
        update(i);
    scanf("%d", &q);
    while (q--) {
        int op, l, r, x;
        scanf("%d%d%d", &op, &l, &r);
        if (op == 1) {
            scanf("%d", &x);
            add(l, r, x);
        }
        else
            printf("%d\n", query(l, r));
    }
    return 0;
}
posted @ 2025-02-10 00:31  quanjun  阅读(62)  评论(0)    收藏  举报