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


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

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

  • 相关阅读:
    [转]ASP.NET会话(Session)保存模式
    ASP.NET 2.0 实现伪静态网页方法
    显示带颜色的字符串
    sublime text 3.0使用
    sublime text插件
    cogs1715 动态逆序对
    双网卡bond
    解决CentOS6不能使用yum源
    查看磁盘io占用
    [office] 在word中的小技巧
  • 原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11765810.html
Copyright © 2020-2023  润新知