[BZOJ]4241 历史研究 回滚莫队

4241: 历史研究

Time Limit: 80 Sec   Memory Limit: 512 MB
Submit: 1030   Solved: 292
[ Submit][ Status][ Discuss]

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)

Source

[ Submit][ Status][ Discuss]


HOME Back


 几句闲话

         orz orz neither_nor(alone_wolf).上次他讲课的时候还没有去看他这道题,后来刷这道题的时候发现他用的是回滚莫队.这道题大多人几乎都用的是分块,然而回滚莫队快的飞起.正好不会回滚莫队,赶紧学习一波.
  题解
    这道题扩大区间更新操作很简单,但是l,r左右 动缩小区间update怎么办?我们就可以用回滚莫队来是区间一直在扩大,回滚莫队讲解:点这里.
      注意离散化和long long.
#include<stdio.h>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long dnt;
const int maxn=100005;
dnt ans[maxn],ed,last,tmp;
int Cnt[maxn],cnt[maxn],blo[maxn],a[maxn],b[maxn],v[maxn],block,n,m,num;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    register int x=0,f=1;
    register char ch=nc();
    while(ch<'0'||ch>'9'){if(ch=='-')f*=-1;ch=nc();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    return f*x;
}
struct query{
    int l,r,id;
}q[maxn];
inline bool cmp(query x,query y){
    if(blo[x.l]==blo[y.l]) return x.r<y.r;
	return blo[x.l]<blo[y.l]; 
}
inline dnt calc(int lf,int rg){
	register int i;ed=0;
	for(i=lf;i<=rg;i++) Cnt[b[i]]=0;
    for(i=lf;i<=rg;i++){
       Cnt[b[i]]++;
       ed=max(ed,(dnt)Cnt[b[i]]*a[i]);
	}
	return ed;
}
inline void update(int x){
    cnt[b[x]]++;tmp=max(tmp,(dnt)cnt[b[x]]*a[x]);
}
inline void re(int x){cnt[b[x]]--;}
inline int Mo(int pos,int bl){
	tmp=0;
	int L=min(block*bl,n);
    register int i=pos,j,ql=L+1,qr=L;
    for(j=1;j<=n;j++) cnt[j]=0;
    for(;blo[q[i].l]==bl;i++){
       if(blo[q[i].l]==blo[q[i].r]){ans[q[i].id]=calc(q[i].l,q[i].r);continue;}
       while(qr<q[i].r) update(++qr);
       last=tmp;
	   while(ql>q[i].l) update(--ql);
	   ans[q[i].id]=tmp;
	   while(ql<L+1) re(ql++);
	   tmp=last;
	}
	return i;
}
int main(){
    n=read(),m=read(),block=sqrt(n);
    register int i,pos=1;
	for(i=1;i<=n;i++) v[i]=a[i]=read();
	sort(v+1,v+n+1);
	int len=unique(v+1,v+n+1)-(v+1);
	for(i=1;i<=n;i++) b[i]=lower_bound(v+1,v+1+len,a[i])-v;
	for(i=1;i<=n;i++) blo[i]=(i-1)/block+1,num=max(blo[i],num);
	for(i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
	sort(q+1,q+1+m,cmp);
	for(i=1;i<=num;i++) pos=Mo(pos,i);
	for(i=1;i<=m;i++) printf("%lld\n",ans[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值