BZOJ4241 历史研究 【回滚莫队】

题目描述:给出一个长度为\(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

#include<bits/stdc++.h>
#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]);
}

猜你喜欢

转载自www.cnblogs.com/AThousandMoons/p/11813002.html