回滚莫队

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

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

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

这样,左端点每次移动不超过\(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\n",ans[i]);
	return 0;
}
posted @ 2020-07-04 21:45  lnzwz  阅读(253)  评论(0编辑  收藏  举报