• 回滚莫队


    很多时候,在进行莫队操作时,会遇到添加容易删除难的问题。
    比如涉及到最值的运算。
    这时,可以使用回滚莫队。

    首先,和普通莫队一样,对序列进行分块。
    对于左右端点在同一个块的询问,直接暴力求解。

    然后,枚举左端点所在的块,并将右端点排序。
    将左端点设为区间右端点,右端点从小到大移动。

    这样,左端点每次移动不超过(O(sqrt n)),移动后将左端点再恢复回区间右端点的位置。
    时间复杂度:(O(m sqrt n))

    例题:

    代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <vector>
    #include <math.h>
    #define ll long long
    using namespace std;
    struct SPx
    {
    	int z,i;
    };
    SPx px[100010];
    int cmp1(const void*a,const void*b)
    {
    	return ((SPx*)a)->z-((SPx*)b)->z;
    }
    int sz[100010],ls[100010],sl[100010];ll ma,ans[100010];
    void add(int x)
    {
    	sl[x]+=1;
    	ll t=1ll*ls[x]*sl[x];
    	if(t>ma)ma=t;
    }
    ll baoli(int l,int r)
    {
    	ma=0;
    	for(int i=l;i<=r;i++)
    		add(sz[i]);
    	ll rt=ma;ma=0;
    	for(int i=l;i<=r;i++)
    		sl[sz[i]]-=1;
    	return rt;
    }
    struct SJd
    {
    	int i,l,r;
    };
    SJd jd[100010];
    vector<SJd> ve[500];
    int cmp2(const void*a,const void*b)
    {
    	return ((SJd*)a)->r-((SJd*)b)->r;
    }
    int ku[100010],st[500],en[500];
    int main()
    {
    	int n,m=0,q;
    	scanf("%d%d",&n,&q);
    	for(int i=0;i<n;i++)
    	{
    		scanf("%d",&px[i].z);
    		px[i].i=i+1;
    	}
    	qsort(px,n,sizeof(SPx),cmp1);
    	for(int i=0;i<n;i++)
    	{
    		if(i==0||px[i].z>px[i-1].z)
    			m+=1;
    		sz[px[i].i]=m;ls[m]=px[i].z;
    	}
    	int B=int(sqrt(n)+0.5),k=0;
    	while(1)
    	{
    		st[k+1]=en[k]+1;k+=1;
    		en[k]=st[k]+B-1;
    		if(en[k]>=n)
    		{
    			en[k]=n;
    			break;
    		}
    	}
    	for(int i=1;i<=k;i++)
    	{
    		for(int j=st[i];j<=en[i];j++)
    			ku[j]=i;
    	}
    	for(int i=0;i<q;i++)
    	{
    		SJd t;t.i=i;
    		scanf("%d%d",&t.l,&t.r);
    		if(ku[t.l]==ku[t.r])
    			ans[i]=baoli(t.l,t.r);
    		else
    			ve[ku[t.l]].push_back(t);
    	}
    	for(int i=1;i<=k;i++)
    	{
    		int z=ve[i].size();
    		for(int j=0;j<z;j++)
    			jd[j]=ve[i][j];
    		qsort(jd,z,sizeof(SJd),cmp2);
    		int l=en[i],r=l-1;ma=0;
    		for(int j=1;j<=m;j++)
    			sl[j]=0;
    		for(int j=0;j<z;j++)
    		{
    			while(r<jd[j].r)
    			{
    				r+=1;
    				add(sz[r]);
    			}
    			ll od=ma;
    			while(l>jd[j].l)
    			{
    				l-=1;
    				add(sz[l]);
    			}
    			ans[jd[j].i]=ma;ma=od;
    			while(l<en[i])
    			{
    				sl[sz[l]]-=1;
    				l+=1;
    			}
    		}
    	}
    	for(int i=0;i<q;i++)
    		printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Maria 与Ann的故事
    引语
    Preface
    Chapter 1 Foundation
    Roman to Integer
    Integer to Roman
    Container with most water
    palindrome number
    String to Integer (atoi)
    Reverse Integer
  • 原文地址:https://www.cnblogs.com/lnzwz/p/13236826.html
Copyright © 2020-2023  润新知