其他分享
首页 > 其他分享> > 【洛谷P5906】【模板】回滚莫队&不删除莫队

【洛谷P5906】【模板】回滚莫队&不删除莫队

作者:互联网

题目

题目链接:https://www.luogu.com.cn/problem/P5906
给定一个序列,多次询问一段区间 \([l,r]\),求区间中相同的数的最远间隔距离

序列中两个元素的间隔距离指的是两个元素下标差的绝对值

思路

显然用一般的莫队处理这种问题时,要删除一个元素时并不可以做到 \(O(1)\) 删除。
考虑莫队是怎么排序的?将左端点按块排序,同一个块的按照右端点从小到大排序。所以最终我们的询问的右端点一定是不超过 \(\sqrt{n}\) 个连续递增的块。
我们可以将每一个右端点递增的块分开来做,具体的,假设现在左端点的区间为 \((kT,(k+1)T]\),其中 \(T=\sqrt{n}\),那么我们就将 \(((k+1)T,r]\) 的区间利用左端点固定,右端点单调不减得性质做,对于询问的另一部分 \([l,(k+1)T]\),由于这一段的长度不超过 \(\sqrt{n}\),所以直接暴力跑就可以了。
对于每一个块,做完之后直接 \(O(n)\) 清空即可。
时间复杂度 \(O(m\sqrt{n})\)。

代码

#include <bits/stdc++.h>
using namespace std;

const int N=200010;
int n,m,T,a[N],b[N],bel[N],last[3][N],ans[N];

struct Query
{
	int l,r,id;
}ask[N];

bool cmp(Query x,Query y)
{
	return bel[x.l]==bel[y.l] ? x.r<y.r : bel[x.l]<bel[y.l];
}

int main()
{
	scanf("%d",&n);
	T=sqrt(n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i]; bel[i]=(i-1)/T+1;
	}
	sort(b+1,b+1+n);
	int tot=unique(b+1,b+1+n)-b-1;
	for (int i=1;i<=n;i++)
		a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&ask[i].l,&ask[i].r);
		ask[i].id=i;
	}
	sort(ask+1,ask+1+m,cmp);
	int ans0,ans1,lim;
	memset(last[2],-1,sizeof(last[2]));
	for (int i=1,r;i<=m;i++)
	{
		if (bel[ask[i].l]!=bel[ask[i-1].l])
		{
			memset(last,-1,sizeof(last));
			r=T*bel[ask[i].l]+1; ans0=0;
		}
		for (;r<=ask[i].r;r++)
		{
			if (last[0][a[r]]==-1) last[0][a[r]]=r;
			last[1][a[r]]=r;
			ans0=max(ans0,last[1][a[r]]-last[0][a[r]]);
		}
		ans1=0; lim=min(T*bel[ask[i].l],ask[i].r);
		for (int j=ask[i].l;j<=lim;j++)
		{
			if (last[2][a[j]]==-1) last[2][a[j]]=j;
			ans1=max(ans1,max(last[1][a[j]]-j,j-last[2][a[j]]));
		}
		for (int j=ask[i].l;j<=lim;j++)
			last[2][a[j]]=-1;
		ans[ask[i].id]=max(ans0,ans1);
	}
	for (int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}

标签:回滚,洛谷,bel,int,sqrt,端点,Query,莫队
来源: https://www.cnblogs.com/stoorz/p/14028871.html