BZOJ 4241 历史研究 (回滚莫队)

来源:互联网 发布:淘宝网的域名怎么设置 编辑:程序博客网 时间:2024/05/19 16:49

4241: 历史研究

Time Limit: 80 Sec Memory Limit: 512 MB
Description

IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
日记中记录了连续N天发生的时间,大约每天发生一件。
事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大。
JOI教授决定用如下的方法分析这些日记:
1. 选择日记中连续的一些天作为分析的时间段
2. 事件种类t的重要度为t*(这段时间内重要度为t的事件数)
3. 计算出所有事件种类的重要度,输出其中的最大值
现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。
4.
Input

第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。
接下来一行N个空格分隔的整数X1…XN,Xi表示第i天发生的事件的种类

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)

题目大意:
有一个长度为n的序列。
有m个询问,每次询问l~r范围内每个数值乘以该数值出现次数的最大值。

思路:
听起来很玄奥的回滚莫队。
回滚莫队可以代替掉删除操作,让时间复杂度仍然保持在O(nsqrtn)。
分块和排序都按照基础莫队做法来,然后在统计答案的时候,如果一个询问的左端点和右端点在同一个块内,那就暴力统计。
然后对于左端点在同一块内的询问我们一起统计,首先让左端点在这一块的最右端,然后让右端点正常向右扩张。当右端点满足条件的时候,记录一下这个时候的状态,再把左端点调整到询问的左端点,这个时候统计一下答案,然后再回滚到没有调整左端点的时候的那个状态。然后再做下一个询问就可以了。
莫队。。。真是个卡暴力的好(e xin)方法

#include <cstdio>#include <cstring>#include <cmath> #include <algorithm>#define N 120000#define LL long longusing namespace std;int n, m, block, pos;int a[N], aa[N], place[N], top;LL sum[N], num[N], pre, now, ans[N];struct Query{    int l, r, id;}q[N];bool cmp ( Query aa, Query bb ){//块内r,块外l     return place[aa.l] < place[bb.l] || (place[aa.l] == place[bb.l] && aa.r < bb.r); }void adde(int c){    sum[c] += aa[c];//+-的是aa中的原值     now = max(now, sum[c]);}void del(int c){    sum[c] -= aa[c];}int main(){    scanf("%d%d", &n, &m);    block = (int) sqrt(n);    for(int i=1; i<=n; i++){        scanf("%d", &a[i]);        aa[i] = a[i];    }    sort(aa+1, aa+1+n);    top = unique(aa+1, aa+1+n) - aa;//去重     for(int i=1; i<=n; i++)        a[i] = lower_bound(aa+1, aa+top, a[i]) - aa;//离散化     for(int i=1; i<=m; i++){        scanf("%d%d", &q[i].l, &q[i].r);        q[i].id = i;//query排序     }    for(int i=1; i<=n; i++)        place[i] = (i-1) / block + 1;    sort(q+1, q+1+m, cmp);    int l, r;    for(int i=1; i<=m; i++){        if(place[q[i].l] != place[q[i-1].l]){            memset(sum, 0, sizeof(sum));            pre = now = 0;            l = pos = place[q[i].l] * block + 1;//l放到块的左端点上             r = l - 1;//保证初值为零         }        if(place[q[i].l] == place[q[i].r]){//l,r在同一块中,不移动l,r暴力query             LL cur = 0;            for(int j=q[i].l; j<=q[i].r; j++){                num[a[j]] += aa[a[j]];                cur = max(cur, num[a[j]]);            }            for(int j=q[i].l; j<=q[i].r; j++)//还原                 num[a[j]] -= aa[a[j]];            ans[q[i].id] = cur;            continue;        }        while(r < q[i].r) adde( a[++r] );//添加信息         pre = now;//记录当前状态         while(l > q[i].l) adde( a[--l] );        ans[q[i].id] = now;//记录答案         while(l < pos) del( a[l++] );//还原(回滚)         now = pre;    }    for(int i=1; i<=m; i++)         printf("%lld\n", ans[i]);    return 0;}
原创粉丝点击