线段树维护区间相同两个数的最近距离 牛客练习赛53(E题)

老瞎眼 pk 小鲜肉

题目描述

老瞎眼有一个长度为 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”。

输入

4 2
2 1 3 3
1 2
1 3

输出

-1
3


思维+线段树;

1.我们要维护 a[] 的异或前缀和,因为只要异或前缀和,找到某两个位置的异或前缀和相等,那么这两个位置之间的数异或就为0;

2.知道第一点,这道题就演变成了求最近的相等的数;可以先把要求的区间按r从小到大排序,然后依次求区间里的数是否有相等的数;

3.这道就变成了一道单点修改+区间查询的线段树;last数组表示这个数之前出现的位置,知道了这个只要把last[i]+1这个值单点修改为区间宽度就行;线段树树区间维护最小值;

4.last切记开到200万;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define lson k<<1
#define rson k<<1|1
#define N 500100
#define M 2000010
using namespace std;
int a[N],last[M],s[N];
int x,y,sum;
struct Nod{
    int l,r,post;
}dian[N];
bool cmp(Nod p,Nod q){
    return p.r<q.r;
}
struct Node{
    int l,r,w;
}tree[N<<2];
inline void build(int k,int ll,int rr){
    tree[k].l=ll,tree[k].r=rr,tree[k].w=2e9;
    if(ll==rr) return;
    int mid=(ll+rr)>>1;
    build(lson,ll,mid);
    build(rson,mid+1,rr);
}
inline void update(int k){
    if(tree[k].l==tree[k].r){
        tree[k].w=y;
        return;
    }
    int mid=(tree[k].l+tree[k].r)>>1;
    if(x<=mid) update(lson);
    else update(rson);
    tree[k].w=min(tree[lson].w,tree[rson].w);
}
inline int ask(int k){
    if(tree[k].l>=x&&tree[k].r<=y){
        return tree[k].w;
    }
    int mid=(tree[k].l+tree[k].r)>>1;
    int re=2e9;
    if(x<=mid) re=min(re,ask(lson));
    if(y>mid) re=min(re,ask(rson));
    return re;
}
int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    build(1,1,n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]^=a[i-1];
    }
    memset(last,-1,sizeof(last));
    last[0]=0;
    for(int i=1;i<=q;i++){
        scanf("%d%d",&dian[i].l,&dian[i].r);
        dian[i].post=i;
    }
    sort(dian+1,dian+q+1,cmp);//按r从小到大排
    int i=1;
    for(int j=1;j<=q;j++){//q个区间
        for(;i<=dian[j].r;i++){//这个i不是从1开始,而是一直往后走 
            if(last[a[i]]!=-1){
                x=last[a[i]]+1;
                y=i-last[a[i]];
                //cout<<y<<endl;
                update(1);
            }
            last[a[i]]=i;
        }
        x=dian[j].l,y=dian[j].r;
        sum=ask(1);
        if(sum!=2e9) s[dian[j].post]=sum;
        else s[dian[j].post]=-1;
    }
    for(int i=1;i<=q;i++) printf("%d\n",s[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值