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[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;
}