SP20644 ZQUERY - Zero Query
题面传送门
考虑前缀和后转化为区间最长相等数距离。
那么可以回滚莫队解决。
回滚莫队是什么呢?适用于一些只能增加而很难减少的情况。
将莫队左端点在一个块时,右端点升序排序,同时维护最左和最右即可。
代码实现:
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,head,a[200039],l,r,ans[200039],an;
int f[200039],fs[200039],flag[200039];
struct ques{int x,y,id;}s[200039];
inline bool cmp(ques x,ques y){return x.x/k==y.x/k?x.y<y.y:x.x<y.x;}
inline void swap(int &x,int &y){x^=y^=x^=y;}
int main(){
freopen("1.in","r",stdin);
register int i,j,h,b;
scanf("%d%d",&n,&m);k=sqrt(n);
for(i=1;i<=n;i++) scanf("%d",&a[i]),a[i]+=a[i-1];
for(i=0;i<=n;i++) a[i]+=n;
for(i=1;i<=m;i++){
scanf("%d%d",&x,&y);
if(x/k==y/k){
an=0;
for(j=x-1;j<=y;j++)f[a[j]]?(an=max(j-f[a[j]],an)):(f[a[j]]=j);
ans[i]=an;
for(j=x-1;j<=y;j++) f[a[j]]=0;
}
else s[++head]=(ques){x-1,y,i};
}
sort(s+1,s+head+1,cmp);
for(i=1;i<=head;i++){
for(j=i;j<=head;j++) if(s[i].x/k!=s[j].x/k)break;
j--;l=s[i].x/k*k+k+1;an=0;
for(h=i;h<=j;h++){
for(;l<=s[h].y;l++)f[a[l]]?(an=max(l-f[a[l]],an)):(f[a[l]]=l),fs[a[l]]=l;
ans[s[h].id]=an;
for(b=s[h].x/k*k+k;b>=s[h].x;b--)fs[a[b]]?(an=max(an,fs[a[b]]-b)):(fs[a[b]]=b,flag[b]=1);
for(b=s[h].x/k*k+k;b>=s[h].x;b--)fs[a[b]]=(flag[b]?0:fs[a[b]]),flag[b]=0;
swap(ans[s[h].id],an);
}
i=j;memset(f,0,sizeof(f));memset(fs,0,sizeof(fs));
}
for(i=1;i<=m;i++) printf("%d\n",ans[i]);
}