有多少人跟我一样看成了滚回莫队的举个爪
先放一道例题:(JOIAT1219)歴史の研究
题意:设(Cnt_i)为(lsim r)这个区间(i)出现的次数,有(m)次询问,求(lsim r)的(max{Val_i*Cnt_i})。
直接考虑莫队,因为要统计一种元素出现的个数。
我们发现,增加操作很好做,但是删除时就无法维护(max)了,这时我们考虑维护答案尽量不用删除操作。
接下来就是我们的回滚莫队了。
先分块,再排序。如果(x.l==y.l),按(x.r<y.r)排序,否则按(x.l,y.l)所在的块升序排序。
将每个块分开处理,如果一个询问两端都在块中间,直接暴力即可。
我们接下来就要处理一段在块中间一段不在的询问了。
设(l=R[T]+1,r=R[T])((T)为当前块)显然右端点是单调递增的,直接跑就可以了,全部都是增加操作。
左端点怎么办?它是无序的啊。。。
没事儿,我们先通过增加让左端点到当前询问的(l)(这个过程中维护答案),再用删除操作使之回到(R[T]+1)即可(这个过程中不维护答案,仅处理信息)。
这样我们就做完了,撒花~
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int f=1,w=0;char x=0;
while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
return w*f;
}
const int N=1e5+10;
int n,m,Siz,Tot;
int Num[N],Col[N],A[N],ans[N],Cnt[N];
struct Query{int l,r,Id;} Q[N];
inline bool Cmp(Query x ,Query y) {return (Col[x.l]^Col[y.l])?Col[x.l]<Col[y.l]:x.r<y.r;}
inline int Force(int l,int r)
{
static int Tim[N];int Now=0;
for(int i=l;i<=r;i++) Tim[Num[i]]=0;
for(int i=l;i<=r;i++) Tim[Num[i]]++,Now=max(Tim[Num[i]]*A[Num[i]],Now);
return Now;
}
inline int Work(int i,int Id)
{
int R=min(Siz*Id,n),lef=R+1,rig=R,Ans=0,Cur=0;
memset(Cnt,0,sizeof(Cnt));
for(;Col[Q[i].l]==Id;i++)
{
if(Col[Q[i].l]==Col[Q[i].r]) {ans[Q[i].Id]=Force(Q[i].l,Q[i].r);continue;}
while(rig<Q[i].r) Ans=max(Ans,(++Cnt[Num[++rig]])*A[Num[rig]]);Cur=Ans;
while(lef>Q[i].l) Ans=max(Ans,(++Cnt[Num[--lef]])*A[Num[lef]]);ans[Q[i].Id]=Ans;
while(lef<R+1) --Cnt[Num[lef++]]; Ans=Cur;
}
return i;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("A.in","r",stdin);
#endif
n=read(),m=read();Siz=sqrt(n);
for(int i=1;i<=n;i++) Num[i]=A[i]=read(),Col[i]=(i-1)/Siz+1;//**
sort(A+1,A+n+1);Tot=unique(A+1,A+n+1)-A-1;
for(int i=1;i<=n;i++) Num[i]=lower_bound(A+1,A+n+1,Num[i])-A;
//Use->A Now->Num;
for(int i=1;i<=m;i++) Q[i].l=read(),Q[i].r=read(),Q[i].Id=i;
sort(Q+1,Q+m+1,Cmp);
for(int i=1,Now=1;i<=Col[n];i++) Now=Work(Now,i);
for(int i=1;i<=m;i++) printf("%lld
",ans[i]);
}
再来看一道例题:(Rmq) (Problem) (/) (mex)
经过分析,我们发现这一题只能删除而不好增加(虽然数据水,暴力维护也能过。。。)
考虑回滚莫队。
其实对于这种莫队,如果(x.l==y.l),按(x.r>y.r)排序,否则按(x.l,y.l)所在的块升序排序。
再设(l=L[R],r=n)就可以做了。。。
具体操作和上面一毛一样。。。
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int f=1,w=0;char x=0;
while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
return w*f;
}
const int N=2e5+10;
int n,m,Siz,Tot;
int A[N],Num[N];
int Col[N],Cnt[N],ans[N];
struct Query{int l,r,Id;} Q[N];
inline bool Cmp(Query x,Query y) {return (Col[x.l]^Col[y.l])?Col[x.l]<Col[y.l]:x.r>y.r;}
inline int Force(int l,int r)
{
int Tim[N],Tmp=0;
memset(Tim,0,sizeof(Tim));
for(int i=l;i<=r;i++) Tim[Num[i]]++;
while(Tim[Tmp]) Tmp++; return Tmp;
}
inline void Delete(int x,int &Min)
{
if(!(--Cnt[Num[x]])&&Num[x]<Min) Min=Num[x];
}
inline int Work(int i,int Id)
{
int L=Siz*(Id-1)+1,l=L,r=n,Tmp=0,Ans;
memset(Cnt,0,sizeof(Cnt));
for(int i=l;i<=r;i++) Cnt[Num[i]]++;
while(Cnt[Tmp]) Tmp++;Ans=Tmp;
for(;Col[Q[i].l]==Id;i++)
{
if(Col[Q[i].l]==Col[Q[i].r]) {ans[Q[i].Id]=Force(Q[i].l,Q[i].r);continue;}
while(r>Q[i].r) Delete(r--,Ans);int Cur=Ans;
while(l<Q[i].l) Delete(l++,Ans);ans[Q[i].Id]=Ans;
while(l>L) Cnt[Num[--l]]++;Ans=Cur;
}
return i;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("A.in","r",stdin);
#endif
n=read(),m=read();Siz=sqrt(n);
//for(int i=1;i<=n;i++) Num[i]=A[i]=read(),Col[i]=(i-1)/Siz+1;
//sort(A+1,A+n+1); Tot=unique(A+1,A+n+1)-A-1;
//for(int i=1;i<=n;i++) Num[i]=lower_bound(A+1,A+n+1,Num[i])-A;
for(int i=1;i<=n;i++) Num[i]=read(),Col[i]=(i-1)/Siz+1;
for(int i=1;i<=m;i++) Q[i].l=read(),Q[i].r=read(),Q[i].Id=i;
sort(Q+1,Q+m+1,Cmp);
for(int i=1,Now=1;i<=Col[n];i++) Now=Work(Now,i);
for(int i=1;i<=m;i++) printf("%d
",ans[i]);
}
别(D)我没打离散化,我是真的懒得写了。。。