链接:Xor sum
题意:
给一个长度为 n 的数组 , 要求出一个最短的连续子段,使它的异或和大于等于 k。
思路:
首先我们要知道 [1 , l]的前缀异或和与 [l , r]的前缀异或和 的异或为 [l + 1 , r] 的异或和。所以我们对于每一个右端点,用字典树查询左边的每一个前缀异或和 , 同时字典树存当前01串出现的最右位置。
代码:
#include <iostream>
#include <cstdio>
#include <queue>
#include <math.h>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 4e6 + 7;
int T;
int tot = 1,tire[maxn][3],pre[maxn];
int n , k , a[maxn];
void insert(int pos , int x) {
int p = 1,t;
for(int i = 30; i >= 0; i --){
t = (x >> i) & 1;
if(tire[p][t] == 0) {
tire[p][t]= ++ tot;
}
p=tire[p][t];
pre[p] = max(pre[p] , pos);
}
}
int find(int pos ,int now , int x){
if(pos == -1) return pre[now];
if(now == 0) return 0;
int l = tire[now][0];
int r = tire[now][1];
if((k >> pos) & 1){
if((x >> pos) & 1){
return find(pos - 1 , l , x);
}
return find(pos - 1 , r , x);
}
else{
if((x >> pos) & 1){
return max(find(pos - 1 , r , x) , pre[l]);
}
return max(find(pos - 1 , l , x) , pre[r]);
}
}
void init(){
for(int i = 0; i <= tot; i ++){
tire[i][1] = tire[i][0] = 0;
pre[i] = 0;
}
tot = 1;
}
int main() {
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
init();
for(int i = 1; i <= n; i ++){
scanf("%d",&a[i]);
}
int ans = 1e9 , pos = -1;
insert(0 , 0);
for(int i = 1; i <= n; i ++){
a[i]^=a[i - 1];
int p = find(30 , 1 , a[i]);
if(i - p < ans && p != 0){
ans = i - p;
pos = p + 1;
}
insert(i , a[i]);
}
if(pos == -1){
printf ("-1\n");
}
else{
printf ("%d %d\n",pos , pos + ans - 1);
}
}
return 0;
}