bzoj4241: 历史研究 回滚莫队

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/83591188

bzoj4241: 历史研究

Description

IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
日记中记录了连续N天发生的时间,大约每天发生一件。
事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大。
JOI教授决定用如下的方法分析这些日记:

  1. 选择日记中连续的一些天作为分析的时间段
  2. 事件种类t的重要度为t*(这段时间内重要度为t的事件数)
  3. 计算出所有事件种类的重要度,输出其中的最大值
    现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

Input

第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。
接下来一行N个空格分隔的整数X1…XN,Xi表示第i天发生的事件的种类
接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数Ai和Bi,表示第i次询问的区间为[Ai,Bi]。

Output

输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度

Sample Input

5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4

Sample Output

9
8
8
16
16

HINT

1<=N<=10^5
1<=Q<=10^5
1<=Xi<=10^9 (1<=i<=N)

分析

最大值这种操作莫队肯定是hold不住的。
原因是只能支持 O ( 1 ) O(1) 插入不能支持 O ( 1 ) O(1) 删除。
于是有了回滚莫队这种神奇的操作。
考虑莫队的时候 r r 是单调的,所以 r r 的操作可以保证只有插入(没换一次块重置即可)。
这时候发现 l l 每次挪动是不超过 n \sqrt n
假设当前所有询问的 l l 分属同一个块,那么每一次都暴力地从这个块的右端点挪到 l l ,这样只有插入操作,可以求出 M x Mx
这个时候有一种操作叫做回滚。虽然说最大值不能 O ( 1 ) O(1) 删除,但是桶可以。只要把这一次操作的 M x Mx 消除,暴力“回滚”到原来的右端点即可。
这样每次左端点暴力的复杂度是 O ( n ) O(\sqrt n) ,总复杂度还是 O ( n n ) O(n\sqrt n)
莫队真是博大精深啊。

代码

#include<bits/stdc++.h>
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
const int N = 1e5 + 10;
struct Q {int l, r, id;}q[N];
int a[N], b[N], l[N], r[N], id[N], nn, n, m, B; long long c[N], A[N];
bool cmp(Q a, Q b) {return id[a.l] == id[b.l] ? a.r < b.r : id[a.l] < id[b.l];}
int F(int x) {
    int L = 1, R = nn, m;
    for(;L != R; b[m = L + R >> 1] >= x ? R = m : L = m + 1) ;
    return L;
}
int main() {
    n = ri(); m = ri(); B = sqrt(n) + 1;
    for(int i = 1;i <= n; ++i) 
        a[i] = b[i] = ri();
    for(int i = 1;i <= m; ++i) 
        q[i].l = ri(), q[i].r = ri(), q[i].id = i;
    std::sort(b + 1, b + n + 1); nn = 1;
    for(int i = 2;i <= n; ++i) 
        if(b[i] != b[i - 1]) b[++nn] = b[i];
    for(int i = 1;i <= n; ++i) 
        a[i] = F(a[i]);
    for(int i = 1;i <= n; ++i) 
        id[i] = (i - 1) / B + 1, r[id[i]] = i;
    std::sort(q + 1, q + m + 1, cmp); long long mx;
    for(int x = 1, ls = 0, L, R;x <= m; ++x) {
        int st = q[x].l, ed = q[x].r; long long *Ans = &A[q[x].id];
        if(id[st] != ls) {
            std::memset(c, 0, sizeof(c));
            ls = id[st]; R = r[id[st]] + 1;
            mx = c[a[R]] = b[a[R]];
        }
        if(id[st] == id[ed]) {
            c[a[R]] = 0; 
            for(int i = st; i <= ed; ++i)
                c[a[i]] += b[a[i]], *Ans = std::max(*Ans, c[a[i]]);
            for(int i = st; i <= ed; ++i)
                c[a[i]] -= b[a[i]];
            c[a[R]] = b[a[R]];
        }
        else {
            for(;R < ed;) 
                ++R, c[a[R]] += b[a[R]], mx = std::max(mx, c[a[R]]);
            *Ans = mx;
            for(int i = r[id[st]]; i >= st; --i)
                c[a[i]] += b[a[i]], *Ans = std::max(*Ans, c[a[i]]);
            for(int i = r[id[st]]; i >= st; --i)
                c[a[i]] -= b[a[i]];
        }
    }
    for(int i = 1;i <= m; ++i) printf("%lld\n", A[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/83591188