**题意:**给你一串数组a,问连续区间异或和<=k的区间,输出这个区间的范围L,R,要求这个区间最小,如果区间相同,则输出L最小的那一个。
题解:
- 根据性质b^b=0,所以xor_sum[i,j] = xor_sum[1,i-1] ^ xor_sum[1,j],所以用前缀异或和进行维护。
- 前缀异或和,我们先在字典树进行查询当前1-i区间异或和,在查找的时候,我们进行判断是否大于等于K,然后存入最小的下标。
- 在查询过后,在将当前异或和插入到字典树中。这样就能完成O(nlogn)的全部扫描了。
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int n, k;
const int N = 100050;
int trie[N * 33][3], tot = 1;
int End[N * 33];
void insert_trie(ll x, int pos)
{
int p = 1;
for (int i = 30; i >= 0; i--)
{
int ch = (x >> i) & 1;
if (trie[p][ch] == 0)
trie[p][ch] = ++tot;
p = trie[p][ch];
}
End[p] = pos; //标记编号
}
ll search_trie(ll x)
{
ll res = 0;
ll ans = -1;
int p = 1;
for (int i = 30; i >= 0; i--)
{
int ch = (x >> i) & 1;
if (res + (1 << i) >= k && End[trie[p][ch ^ 1]]) //寻找最小的符合条件的编号
ans = max(ans, (ll)End[trie[p][ch ^ 1]]);
if (res >= k && End[trie[p][ch]])
ans = max(ans, (ll)End[trie[p][ch]]);
if (trie[p][ch ^ 1])
{
res += 1 << i;
p = trie[p][ch ^ 1];
}
else
{
p = trie[p][ch];
}
}
return ans;
}
void init()
{
for (int i = 0; i <= n * 32 + 5; i++)
{
End[i] = 0;
for (int j = 0; j <= 2; j++)
{
trie[i][j] = 0;
}
}
tot = 1;
}
void solve()
{
scanf("%d%d", &n, &k);
init();
ll tmp = 0;
ll a;
int len = 0x7f7f7f7f;
int l = 0, r = 0;
insert_trie(0, 1); //将0点进行插入
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a);
tmp ^= a;
int tmpl = search_trie(tmp);
insert_trie(tmp, i + 1);
if (tmpl != -1 && len > (i - tmpl + 1))
{
len = i - tmpl + 1;
l = tmpl, r = i;
}
}
if (len == 0x7f7f7f7f)
printf("-1\n");
else
printf("%d %d\n", l, r);
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
solve();
}
return 0;
}