回滚莫队

算法原理

  • 分块排序:和普通莫队算法一样,先将数列分成大小约为\(\sqrt{n}\)的块,然后对查询区间按照左端点所在的块进行排序,如果左端点在同一个块内,则按照右端点从小到大排序。
  • 分块处理:对于左端点在同一个块内的所有查询区间,以该块的右端点为基准,将查询区间的处理分为两部分:
    -对于左端点和右端点都在当前块内的查询区间,直接暴力求解。
    -对于左端点在当前块内,右端点在块外的查询区间,固定左端点从块的最右端开始向左移动,同时不断扩展右端点,在处理完一个查询后,将右端点扩展到最大,然后将左端点回滚到块的最右端,为处理下一个查询做准备。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1000005;
int n, m, B, block[N];
ll a[N], b[N];
ll res, last, ans[N], cnt[N], c[N];
struct node
{
    int l, r, id;
} q[N];

bool cmp(node x,node y)
{
    if (block[x.l] != block[y.l])
            return x.l < y.l;
        return x.r < y.r;
}
void add(int x)
{ // 加上一个数的贡献
    ++cnt[x];
    res = max(res, cnt[x] * b[x]);
}
ll calc(int l, int r)
{ // 暴力计算块内
    ll mx = 0;
    for (int i = l; i <= r; ++i)
        c[a[i]] = 0;
    for (int i = l; i <= r; ++i)
    {
        ++c[a[i]];
        mx = max(mx, c[a[i]] * b[a[i]]);
    }
    return mx;
}
int main()
{
    scanf("%d%d", &n, &m);
    B = sqrt(n); // 块长
    for (int i = 1; i <= n; ++i)
        scanf("%lld", a + i), b[i] = a[i], block[i] = (i - 1) / B + 1; // i在哪个块
    int num = block[n];                                   // 块数
    sort(b + 1, b + n + 1,cmp);
    for (int i = 1; i <= n; ++i) // 离散化a
        a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
    for (int i = 1, l, r; i <= m; ++i)
        scanf("%d%d", &l, &r), q[i] = {l, r, i};
    sort(q + 1, q + m + 1);
    for (int i = 1, x = 1; i <= num; ++i)
    { // 第i块
        res = last = 0;
        for (int j = 1; j <= n; ++j)
            cnt[j] = 0; // 清空
        int R = min(B * i, n), l = R + 1, r = R;
        for (; block[q[x].l] == i; ++x)
        { // 第i块的查询x
            if (block[q[x].l] == block[q[x].r])
            { // 块内
                ans[q[x].id] = calc(q[x].l, q[x].r);
                continue;
            }
            while (r < q[x].r)
                add(a[++r]); // 右扩展
            last = res;      // 结果存为last
            while (l > q[x].l)
                add(a[--l]);    // 左扩展
            ans[q[x].id] = res; // 结果存入答案
            while (l <= R)
                --cnt[a[l++]]; // 回滚l
            res = last;        // 回滚结果
        }
    }
    for (int i = 1; i <= m; ++i)
        printf("%lld\n", ans[i]);
}
posted @ 2025-02-28 13:39  流氓兔LMT  阅读(74)  评论(0)    收藏  举报