回滚莫队
算法原理
- 分块排序:和普通莫队算法一样,先将数列分成大小约为\(\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]);
}
本文来自博客园,作者:流氓兔LMT,转载请注明原文链接:https://www.cnblogs.com/-include-lmt/p/18743079

浙公网安备 33010602011771号