回滚莫队——不知道为什么起这个名字的莫队

有多少人跟我一样看成了滚回莫队的举个爪

先放一道例题:\(JOIAT1219\)歴史の研究

题意:设\(Cnt_i\)\(l\sim r\)这个区间\(i\)出现的次数,有\(m\)次询问,求\(l\sim 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\n",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\n",ans[i]);
}

\(D\)我没打离散化,我是真的懒得写了。。。

猜你喜欢

转载自www.cnblogs.com/wo-shi-zhen-de-cai/p/11765810.html