bzoj4241 历史研究 回滚莫队

14 篇文章 0 订阅

Description


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

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

Solution


回滚莫队,get到新姿势了
对于一些能简单插入但是不能简单删除的信息可以考虑回滚莫队

一开始写的是莫队+线段树,计算一波复杂度发现并不能过就gg了

对于询问区间(l,r),我们把起点tl设为l所在块的右端点,tr设为l-1
不断右移右指针tr,记下此时答案为tmp1
不断左移左指针tl,此时答案为询问答案
将左指针tl回滚到起点(l所在块的右端点),将当前答案恢复为tmp1
如果当前询问的l与上一次询问的l所在块相同就保留左右指针,否则重新调整起点

注意到我们对询问的排序方式非常特殊,这种做法能够避开删除操作并且保持优秀的复杂度

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef long long LL;
const int N=100005;

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

int a[N],b[N],bel[N],B;
LL ans[N],vec[N],rec[N];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

bool cmp(Q a,Q b) {
    return bel[a.l]<bel[b.l]||bel[a.l]==bel[b.l]&&(a.r<b.r);
}

int main(void) {
    int n=read(),m=read(); B=sqrt(n);
    rep(i,1,n) {
        a[i]=read();
        b[i]=a[i]; bel[i]=(i-1)/B+1;
    }
    std:: sort(b+1,b+n+1);
    int size=std:: unique(b+1,b+n+1)-b-1;
    rep(i,1,n) a[i]=std:: lower_bound(b+1,b+size+1,a[i])-b;
    rep(i,1,m) q[i]=(Q) {read(),read(),i};
    std:: sort(q+1,q+m+1,cmp);
    int l=1,r=0;
    LL tmp1=0,tmp2=0,pos;
    rep(i,1,m) {
        if (bel[q[i].l]!=bel[q[i-1].l]) {
            fill(rec,0);
            pos=l=bel[q[i].l]*B+1,r=l-1;
            tmp1=tmp2=0;
        }
        if (bel[q[i].l]==bel[q[i].r]) {
            rep(j,q[i].l,q[i].r) {
                vec[a[j]]+=b[a[j]];
                ans[q[i].id]=std:: max(ans[q[i].id],vec[a[j]]);
            }
            rep(j,q[i].l,q[i].r) vec[a[j]]-=b[a[j]];
            continue;
        }
        for (;r<q[i].r;) { r++;
            rec[a[r]]+=b[a[r]];
            tmp1=std:: max(tmp1,rec[a[r]]);
        }
        tmp2=tmp1;
        for (;l>q[i].l;) { l--;
            rec[a[l]]+=b[a[l]];
            tmp1=std:: max(tmp1,rec[a[l]]);
        }
        ans[q[i].id]=tmp1;
        for (;l<pos;) {
            rec[a[l]]-=b[a[l]]; l++;
        }
        tmp1=tmp2;
    }
    rep(i,1,m) printf("%lld\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值