题目描述
老瞎眼有一个长度为 n 的数组 a,为了为难小鲜肉,他准备了 Q 次询问,每次给出 一个区间[L,R],他让小鲜肉寻 找一对 l,r 使L<=l<=r<=R 且 a[l]a[l+1]a[l+2]…^a[r]=0,老瞎眼只让他回答r-l+1 最小是多少,若没有符合条件的 l,r 输出”-1”。
输入描述:
第一行输入 n,Q。
第二行输入 n 个数,表示 a 数组。
接下来 Q 行,每行输入 L,R。
1<=n,Q<=500000,0<=a[i]<=1000000,1<=L<=R<=n
输出描述:
若有解,输出 r-l+1 最小是多少。
否则输出“-1”。
示例1
输入
复制
4 2
2 1 3 3
1 2
1 3
输出
复制
-1
3
说明
第一次询问无解。
第二次询问:
l=1,r=3
刚看到题目时,没有想到怎么去维护区间最近的位置。
我们如果考虑离线去做,把区间按照右端点排序。
我们提前预处理出数组的异或前缀和。然后我们对于某个区间异或为0,相当于就是前缀异或和中两个点的值相同。
然后我们按照右端点排序之后,保证当前查询的时候,右端点一定是保证满足的,然后我们看左端点的位置即可。所以我们插入元素时,插入到左端点即可。
然后用线段树一直维护区间的最小值。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=5e5+10;
const int inf=0x3f3f3f3f;
int n,Q,sum[N],vis[N<<2],ed,res[N];
struct query{
int l,r,id;
}q[N];
struct node{
int l,r,mi;
}t[N<<2];
int cmp(query a,query b){
return a.r<b.r;
}
inline void push_up(int p){
t[p].mi=min(t[p<<1].mi,t[p<<1|1].mi);
}
void build(int p,int l,int r){
t[p].l=l; t[p].r=r; t[p].mi=inf;
if(l==r) return ; int mid=l+r>>1;
build(p<<1,l,mid); build(p<<1|1,mid+1,r);
}
void change(int p,int x,int v){
if(t[p].l==t[p].r){
t[p].mi=min(t[p].mi,v); return ;
}
int mid=t[p].l+t[p].r>>1;
if(x<=mid) change(p<<1,x,v);
else change(p<<1|1,x,v);
push_up(p);
}
int ask(int p,int l,int r){
if(t[p].l==l&&t[p].r==r) return t[p].mi;
int mid=t[p].l+t[p].r>>1;
if(r<=mid) return ask(p<<1,l,r);
else if(l>mid) return ask(p<<1|1,l,r);
else return min(ask(p<<1,l,mid),ask(p<<1|1,mid+1,r));
}
signed main(){
scanf("%d %d",&n,&Q);
for(int i=1;i<=n;i++) scanf("%d",&sum[i]);
for(int i=1;i<=n;i++) sum[i]^=sum[i-1];
for(int i=1;i<=Q;i++) scanf("%d %d",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+1+Q,cmp); ed=1; build(1,0,n);
memset(vis,-1,sizeof vis); vis[0]=0;
for(int i=1;i<=Q;i++){
for(int j=ed;j<=q[i].r;j++){
if(vis[sum[j]]!=-1) change(1,vis[sum[j]],j-vis[sum[j]]);
vis[sum[j]]=j;
}
ed=q[i].r+1;
res[q[i].id]=ask(1,q[i].l-1,q[i].r);
if(res[q[i].id]==inf) res[q[i].id]=-1;
}
for(int i=1;i<=Q;i++) printf("%d\n",res[i]);
return 0;
}