AT1219 歴史の研究(回滚莫队)

Posted sto324

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AT1219 歴史の研究(回滚莫队)相关的知识,希望对你有一定的参考价值。

题目描述

IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。

日记中记录了连续N天发生的时间,大约每天发生一件。

事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi?表示,Xi?越大,事件的规模就越大。

JOI教授决定用如下的方法分析这些日记:

选择日记中连续的一些天作为分析的时间段

事件种类t的重要度为t*(这段时间内重要度为t的事件数)

计算出所有事件种类的重要度,输出其中的最大值 现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

1<=N<=1051<=Q<=105

1<=Xi?<=109(1<=i<=N)

题解

这类问题很明显可以用莫队,可是注意到本题用莫队只支持扩张区间,而不能缩短区间。

所以就要引用一种神奇的做法:回滚莫队。

回滚莫队和普通莫队一样需要先分块再将询问排序;不同的只是询问左端点在同一个块时,每次将左端点放在该块的右边界,因为右端点单增,所以先扩张右端点记录答案,再扩张左端点询问答案,最后再让左端点滚回有边界。

具体如何实现?

对于每个块需要从头开始统计答案,右端点扩张时对于这个块还没赋予答案的询问都有贡献,当左端点扩张前要记录当前答案,最后回到记录的答案以使得答案也回滚。

本题要注意的是需要离散化;对于询问端点在同一块内时,暴力统计即可;块的大小size不等于块的个数pos[n],就因为这个WA了好久。。

技术图片
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const int maxn=100005;
int n,m;
ll aa[maxn],a[maxn],b[maxn];//原序列,离散化序列,桶 
int size,pos[maxn];
ll ans[maxn],nowans;
struct question
    int l,r,id;
q[maxn];

template<class T>inline void read(T &x)
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48);ch=getchar();


ll max(ll x,ll y)return x>y ? x : y ;

bool cmp(question a,question b)
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];


ll cx[maxn];//暴力使用 

ll nice(int l,int r)
    for(int i=l;i<=r;i++) cx[a[i]]=0;
    ll ret=0;
    for(int i=l;i<=r;i++)
        cx[a[i]]++;
        ret=max(ret,aa[a[i]]*cx[a[i]]);
    
    return ret;


void add(int x)
    b[a[x]]++;
    nowans=max(nowans,aa[a[x]]*b[a[x]]);


void del(int x)
    b[a[x]]--;


int mo(int i,int id)
    int R=min(n,id*size),nowl=R,nowr=nowl-1;
    nowans=0;
    memset(b,0,sizeof(b));
    for(;pos[q[i].l]==id;i++)
        if(pos[q[i].l]==pos[q[i].r]) ans[q[i].id]=nice(q[i].l,q[i].r);continue;
        while(nowr<q[i].r) add(++nowr);
        ll ret=nowans;
        while(nowl>q[i].l) add(--nowl);
        ans[q[i].id]=nowans;
        while(nowl<R) del(nowl++);
        nowans=ret;
    
    return i;


int main()
    read(n);read(m);
    size=sqrt(n+0.5);
    for(int i=1;i<=n;i++)read(aa[i]);a[i]=aa[i];pos[i]=(i-1)/size+1;
    sort(aa+1,aa+n+1);
    int tot=unique(aa+1,aa+n+1)-aa-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(aa+1,aa+tot+1,a[i])-aa;
    for(int i=1;i<=m;i++)read(q[i].l);read(q[i].r);q[i].id=i;
    sort(q+1,q+m+1,cmp);
    for(int i=1,id=1;id<=pos[n];id++)
     i=mo(i,id);
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
View Code

以上是关于AT1219 歴史の研究(回滚莫队)的主要内容,如果未能解决你的问题,请参考以下文章

AT1219 歴史の研究

AT1219 歴史の研究 解题报告

bzoj4241/AT1219 历史研究(回滚莫队)

习题:历史研究(回滚莫队)

历史研究(回滚莫队)(add)

回滚莫队