题意:给定一数组A,问最少连续几个数的异或和大于等于k
解法:
得到A数组的前缀异或数组X,每次询问时固定右端点j,查看j的左方有无i使得X[j]^X[i]>=k,如果有,则取最大的i,如果j-i+1>所记录的答案ans,则更新答案。用01字典树记录前缀异或数值,在01字典树的每个节点设置一个标志flag,记录可以到达这个节点的X[i]的下标i,对于每一个询问端点j,我们在01字典树中做这样的搜索:
①如果k二进制下某位是1,而X[j]是d,那么我们需要询问能否到达该位为d^1的节点,如果不存在,在还未访问过的数均不能能与X[j]异或后大于等于k,如果存在,则继续询问下一位
②如果k二进制下某位是0,而X[j]是d,如果能够到达该位置为d^1的节点,如果存在,则找到了一个异或X[j]使得大于等于k的数,记录其记录的flag,如果不存在,则继续询问下一位。
③当搜索完成后,如果记录有flag的话,由于要使长度最小,所以如果j-flag+1>ans,则更新ans
AcCode:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int xor_sum[N];
int nxt[32 * N][2], index[N * 32];
struct tree {
int cnt = 0;
tree() { nxt[0][0] = nxt[0][1] = 0; };
void insert(int x, int id) {
int pos = 0;
for (int i = 30; i >= 0; i--) {
int d = (x >> i) & 1;
if (!nxt[pos][d]) {
nxt[pos][d] = ++cnt;
index[cnt] = -1;
nxt[cnt][0] = nxt[cnt][1] = 0;
}
pos = nxt[pos][d];
index[pos] = id;
}
}
void getLeft(int x, int k, int id, int& l, int& r) {
int pos = 0;
int ret = -1;
for (int i = 30; i >= 0; i--) {
int d1 = (x >> i) & 1;
int d2 = (k >> i) & 1;
if (d2) {
pos = nxt[pos][d1 ^ 1];
}
else {
if (nxt[pos][d1 ^ 1]) ret = max(ret, index[nxt[pos][d1 ^ 1]]);//走这步可以大于k
pos = nxt[pos][d1];
}
if (!pos) break;//没找过的数不存在使得异或x后大于等于k的数
}
if (pos) ret = max(ret, index[pos]);//pos存在说明走到这一步可以大于等于k
if (ret >= 0 && (r - l + 1) > (id - ret + 1)) r = id, l = ret;
}
};
signed main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
int k;
scanf("%d %d", &n, &k);
int l = -1, r = n;
for (int i = 1; i <= n; i++) {
scanf("%d", &xor_sum[i]);
xor_sum[i] = xor_sum[i - 1] ^ xor_sum[i];
}
tree tire;
for (int i = 0; i <= n; i++) {
tire.getLeft(xor_sum[i], k, i, l, r);
tire.insert(xor_sum[i], i);
}
if (l != -1) printf("%d %d\n", l + 1, r);
else printf("-1\n");
}
}