您的位置:首页 > 其它

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

2017-07-25 14:41 1081 查看

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 long
using namespace std;

int n, m, block, pos;
int a
, aa
, place
, top;
LL sum
, num
, pre, now, ans
;

struct Query{
int l, r, id;
}q
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: