题目
题目链接: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;
}