题目描述:给出一个长度为\(n\)的数组,每次询问区间 \([l,r]\),求 \(\max\limits_{x}x*cnt_x\),其中 \(cnt_x\) 表示 \(x\) 在区间 \([l,r]\) 的出现次数。
数据范围:\(n\le 10^5,a_i\le 10^9\)。
分块也可以做到 \(O(n\sqrt{n})\),但是空间也是 \(O(n\sqrt{n})\)。使用回滚莫队可以在 \(O(n)\) 空间内解决。
首先上来离散化,然后考虑莫队。发现这个东西增加一个数是 \(O(1)\) 的,但删除一个数至少要 \(O(\log n)\) 才能做,所以我们考虑只增。首先询问按照普通莫队的方法排序,然后处理 \(l\) 在一块,而且 \(r\) 递增的询问。设块的右端点为 \(mid\),则 \((mid,r]\) 可以只添加数来解决,而 \([l,mid]\) 这一段直接暴力(长度 \(\sqrt{n}\))。总时间复杂度是 \(O(n\sqrt{n})\) 的。
于是像区间众数这种东西也可以用回滚莫队做了。
code
```cpp
#include
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 100003;
int n, m, blo, x[N], val[N], len, ql, qr, mid;
LL cnt[N], qans, ans[N], tt[N];
struct Query {
int l, r, id;
inline bool operator < (const Query &o) const {
if(l / blo != o.l / blo) return l / blo < o.l / blo;
return r < o.r;
}
} q[N];
inline void add(int x){qans = max(qans, cnt[x] += val[x]);}
inline void del(int x){cnt[x] -= val[x];}
int main(){
scanf("%d%d", &n, &m); blo = sqrt(n);
for(Rint i = 1;i <= n;i ++) scanf("%d", x + i), val[i] = x[i];
sort(val + 1, val + n + 1);
len = unique(val + 1, val + n + 1) - val - 1;
for(Rint i = 1;i <= n;i ++) x[i] = lower_bound(val + 1, val + len + 1, x[i]) - val;
for(Rint i = 1;i <= m;i ++) scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
sort(q + 1, q + m + 1);
for(Rint i = 1;i <= m;i ++){
if(i == 1 || q[i].l / blo != q[i - 1].l / blo){
memset(cnt, 0, sizeof cnt); qans = 0; ql = qr = mid = min(n, (q[i].l / blo + 1) * blo);
}
if(q[i].l / blo == q[i].r / blo){
LL res = 0;
for(Rint j = q[i].l;j <= q[i].r;j ++) res = max(res, tt[x[j]] += val[x[j]]);
ans[q[i].id] = res;
for(Rint j = q[i].l;j <= q[i].r;j ++) tt[x[j]] = 0;
continue;
}
while(qr <= q[i].r) add(x[qr]), ++ qr;
LL tmp = qans;
while(ql > q[i].l) -- ql, add(x[ql]);
ans[q[i].id] = qans;
while(ql < mid) del(x[ql]), ++ ql;
qans = tmp;
}
for(Rint i = 1;i <= m;i ++) printf("%lld\n", ans[i]);
}
```