有多少人跟我一样看成了滚回莫队的举个爪
先放一道例题:\(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\)我没打离散化,我是真的懒得写了。。。
来源:博客园
作者:风骨傲天
链接:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11765810.html