一个很简单的“禁术”。

RMQ

设序列长度为 $n$。

对序列分块,每块长度 $\log_2n$,块数 $n/\log_2n$,记录每块最大值,用 st 表预处理出块件最大值,这一步复杂度为 $O(n)$。

预处理出每一块前后缀最大值,这样当询问的两个端点不在同一个块内,就可以 $O(1)$ 回答了。

现在要处理询问端点在一个块内的情况。

对于一个块的所有前缀,维护一个递减的单调栈,并记录哪些元素在单调栈里(用一个整数状压存储)。

这样 $[l,r]$ 的最大值位置就是前缀 $r$ 的单调栈中第一个在原数组中下标大于等于 $l$ 的位置,即第 $l$ 位后第一个 $1$。

Code

使用了常数略大的写法,为了卡常可以在预处理的时候把多个循环合并为一个。

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

bool MEM1;

namespace solve
{
    const int maxn = 2e7 + 3;

    int len;

    int f[25][maxn / 24 + 7];
    int pre[maxn], suf[maxn], id[maxn], idn, s[maxn];
    int pos[maxn], lg[maxn];

    int n, a[maxn], q;

    void init()
    {
        len = __lg(n);
        lg[0] = -1;
        id[0] = -1;
        static int stk[30], top;
        for (int i = 1; i <= n; i++)
        {
            lg[i] = lg[i >> 1] + 1;
            id[i] = (i - 1) / len + 1;
            if (id[i] == id[i - 1])
                pos[i] = pos[i - 1] + 1;
            else
                pos[i] = 0;
            f[0][id[i]] = max(f[0][id[i]], a[i]);
        }
        idn = id[n];
        for (int i = 1; i <= lg[idn]; i++)
            for (int j = 1; j + (1 << i) - 1 <= idn; j++)
                f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
        for (int i = 1; i <= n; i++)
            if (id[i] != id[i - 1])
                pre[i] = a[i];
            else
                pre[i] = max(pre[i - 1], a[i]);
        for (int i = n; i >= 1; i--)
            if (id[i] != id[i + 1])
                suf[i] = a[i];
            else
                suf[i] = max(suf[i + 1], a[i]);
        for (int i = 1; i <= n; i++)
        {
            if (id[i] != id[i - 1])
                top = 0;
            else
                s[i] = s[i - 1];
            while (top && a[stk[top]] <= a[i])
                s[i] ^= 1 << pos[stk[top--]];
            s[i] |= 1 << pos[stk[++top] = i];
        }
    }

    inline int query(int l, int r)
    {
        int idl = id[l], idr = id[r];
        if (idl != idr)
        {
            int ans = max(suf[l], pre[r]);
            if (idr - idl > 1)
            {
                int d = lg[idr - idl - 1];
                ans = max(ans, max(f[d][idl + 1], f[d][idr - (1 << d)]));
            }
            return ans;
        }
        else
            return a[l + __builtin_ctz(s[r] >> pos[l])];
    }

    void main()
    {
        cin >> n >> q;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        init();
        while (q--)
        {
            int l, r;
            cin >> l >> r;
            cout << query(l, r) << '\n';
        }
    }
}

bool MEM2;

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    solve::main();
}