[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)


solution:回滚莫队 +分块

块内暴力,快外分块乱搞

进第一页了QAQ

这里写图片描述

/**************************************************************
    Problem: 4241
    User: Venishel
    Language: C++
    Result: Accepted
    Time:13264 ms
    Memory:6928 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <cmath>
using namespace std;
#define max(a,b) (a>b?a:b)
#define N 120000

int bl[N], aa[N], a[N];
int n, m, tot, blk, pos;
long long now, pre, sum[N], num[N], ans[N];

struct E{
    int l, r, id;
}q[N];

int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void add( int x ){
    sum[x] += aa[x];
    now = max( now, sum[x] );
}

void del( int x ){
    sum[x] -= aa[x];
}

bool cmp( E a, E b ){
    return bl[a.l]<bl[b.l] || (bl[a.l]==bl[b.l]&&a.r<b.r);
}
void solve(){
    int l=1, r=0;
    for ( int i=1; i<=m; i++ ){
        if ( bl[q[i].l] != bl[q[i-1].l] ) {
            memset( sum, 0, sizeof(sum) );
            pre = now = 0;
            l = pos = bl[q[i].l] * blk + 1;
            r = l-1;
        }
        if ( bl[q[i].l] == bl[q[i].r] ){
            long long 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 ) add( a[++r] );
        pre = now;
        while ( l>q[i].l ) add( a[--l] );
        ans[q[i].id] = now;
        while ( l<pos ) del( a[l++] );
        now = pre;
    }
}
int main(){
    n = read(), m = read();
    blk = (int)sqrt(n);
    for ( int i = 1; i <= n; i++ ) aa[i] = a[i] = read();
    sort( aa+1, aa+1+n );
    tot = unique( aa+1, aa+1+n ) - aa;
    for ( int i=1; i<=n; i++ ) a[i] = lower_bound( aa+1, aa+tot, a[i]) - aa;
    for ( int i=1; i<=m; i++ ) q[i].l = read(), q[i].r = read(), q[i].id=i;
    for ( int i=1; i<=n; i++ ) bl[i] = ( i-1 ) / blk + 1;
    sort( q+1, q+1+m, cmp);
    solve();
    for ( int i=1; i<=m; i++ ) printf( "%lld\n", ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值