题解 P5906 【【模板】回滚莫队&不删除莫队】
寒冰大大
2020-07-07 17:00:20
呜呼呼这个回滚莫队大概是一种正难则反的思路,如果遇到比较毒瘤的删除的话,要不就减少删除的次数,要不就把删除改成插入
恰好这道题都用了,虽然套一个数据结构的莫队比较好做(可惜有个$log$),可是这道题已经写了【模板】回滚莫队&不删除莫队,所以我们至少得用莫队
首先一看就知道用$st,ed$两个桶,每次都记录这个点在$[l,r]$(表示当前问题的左端点和右端点的位置)。
但是如果套普通的套一个莫队,你会发现这东西可以做到$O(1)$修改,但是似做不到O(1)撤销(除非开大空间存下来)
原来的莫队是左端点在每$\sqrt{m}$区间内的问题排序,所以这些询问的左端点都在一个块内部,右端点则是按$r$升序排序(不管奇偶性优化),可见对右端点可以做到没有删除操作,但是左端点则不然,况且左端点只有$\sqrt{m}$个操作,我们对左端点也可以做到没有插入操作,那就是每次从这个块的最右边往左移,每次向左之后要还原就行了。
可以看见每个块内的询问最多向右$\sqrt{m}$次
每个询问最多向左走$\sqrt{m}$次
复杂度还是$O(n\sqrt{m})$
除此之外本题还有一些细节要注意,写在代码注释上了
```cpp
#include<touwenjian.h>
using namespace std;
const int maxn=502000;//用不到这么多的空间
int n,m;
int st[maxn],ed[maxn],looker[maxn],lok[maxn],a[maxn],b[maxn];
int ans;
int bj[maxn];
int len,cnt;
int aans[maxn];
struct que{
int l,r,bh;
}q[maxn];
int cmp(que aa,que bb)
{
if((aa.l-1)/len==(bb.l-1)/len) return aa.r<bb.r;
return aa.l<bb.l;
}
int tst[maxn],ted[maxn],tlooker[maxn],tcnt;
int bl(int l,int r) //这东西得单独开一套变量来
{
int i,ans=0;
for(i=l;i<=r;i++)
{
if(!tst[a[i]]) tst[a[i]]=i;
ted[a[i]]=max(ted[a[i]],i); ans=max(ted[a[i]]-tst[a[i]],ans); tlooker[++tcnt]=a[i];
}
for(i=1;i<=tcnt;i++) tst[tlooker[i]]=ted[tlooker[i]]=0,tlooker[i]=0;
tcnt=0;
return ans;
}
inline void addr(int x) //右端点只有插入,因此可以保留
{
if(!st[a[x]]) st[a[x]]=x;
ed[a[x]]=max(ed[a[x]],x); ans=max(ed[a[x]]-st[a[x]],ans); looker[++cnt]=a[x];
}
inline void addl(int x) //左端点每次都要清空,除了判断答案在左边这个块的情况暂时用一用ed数组(之后也要清空),其他都不能动。
{
if(!ed[a[x]]) ed[a[x]]=x;
else ans=max(ans,ed[a[x]]-x);
}
int main()
{
ios::sync_with_stdio(false);
register int i,j;
cin>>n;
len=sqrt(n);
for(i=1;i<=n;i++) cin>>a[i],b[i]=a[i];
sort(b+1,b+n+1); int tot=unique(b+1,b+n+1)-b-1;
for(i=1;i<=n;i++) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
cin>>m;
for(i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].bh=i;
sort(q+1,q+m+1,cmp);
i=1;
for(j=1;j<=(n-1)/len+1;j++)
{
ans=0;
int baspla=min(len*j,n);
int l=baspla+1,r=baspla;
for(;(q[i].l-1)/len+1==j&&i<=m;i++) //一定要加上i<=m的判断
{
if((q[i].l-1)/len==(q[i].r-1)/len) aans[q[i].bh]=bl(q[i].l,q[i].r);
else
{
while(q[i].r>r) addr(++r); //莫队基本操作
int rigans=ans;
while(q[i].l<l) addl(--l);
while(l<=baspla) { if(ed[a[l]]<=baspla) ed[a[l]]=0; l++;}
aans[q[i].bh]=ans;
ans=rigans;
}
}
for(int k=1;k<=cnt;k++) st[looker[k]]=ed[looker[k]]=0,looker[k]=0;
//记得清空
cnt=0;
}
for(i=1;i<=m;i++) cout<<aans[i]<<endl;
return 0;
}
```