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

匿名 (未验证) 提交于 2019-12-03 00:15:02

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

先放一道例题:\(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\)我没打离散化,我是真的懒得写了。。。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!