题意简述
给(n)个数(a_1,a_2,cdots,a_n),有(m)个操作,每次操作给一个数(x),要求:
- 将所有数(operatorname{xor} space x)
- 求此时数列的( ext{mex})值
( ext{mex})指当前数列未出现的最小非负整数。
数据范围自行查看题目。
简单口胡
先考虑一下没有第一个(operatorname{xor})操作的时候怎么做。我们将所有数插入一个01-trie中,对于每一位我们都想:最好往0走。但是什么时候是不能往0走的呢?
如果左子树(即0节点的子树)是一个满二叉树,说明如果走到0节点,那么就没有不出现的数了,而我们要找的是( ext{mex}),违背定义,这时候只能退而求其次,走1节点。
加上了(operatorname{xor})其实也是差不多的,异或具有结合律,即(a operatorname{xor} x_1 operatorname{xor} x_2 = a operatorname{xor} (x_1 operatorname{xor} x_2)),所以记录(s)为当前的(operatorname{xor})和,如果第(i)位(s)为1,那么就是说明01-trie的第(i)层要取反,那么也就是最好往1走,如果是0那就是一样的。
写的话还是看水平,反正我写了好久/kk
# include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int n,m;
int a[N];
int s;
struct _01trie
{
int T[N << 2][2],siz[N << 2];
int tot = 0;
void insert(int x)
{
int root = 0;
for(int i = 20; i >= 0; i--)
{
int p = (x >> i) & 1;
if(T[root][p] == 0) T[root][p] = ++tot;
root = T[root][p];
}
return;
}
void dfs(int root)
{
if(T[root][0] == 0 && T[root][1] == 0)
{
siz[root] = 1;
return;
}
if(T[root][0]) dfs(T[root][0]);
if(T[root][1]) dfs(T[root][1]);
siz[root] += siz[T[root][0]] + siz[T[root][1]] + 1;
// printf("root = %d,siz[%d] = %d
",root,root,siz[root]);
return;
}
int query(void)
{
int root = 0;
int ans = 0;
for(int i = 20; i >= 0; i--)
{
int p = (s >> i) & 1;
// if(p == 1)
// {
// printf("i = %d
",i);
// printf("root = %d
",root);
// printf("T = %d
",T[root][p]);
// printf("siz = %d
",siz[T[root][p]]);
// }
if(T[root][p] != 0 && siz[T[root][p]] == (1 << (i + 1)) - 1)
{
// printf("root = %d
",root);
root = T[root][p ^ 1],ans += (1 << i);
}
else root = T[root][p];
if(!root) break;
}
return ans;
}
}Trie;
int main(void)
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
sort(a + 1,a + n + 1);
int Newn = unique(a + 1,a + n + 1) - a - 1;
for(int i = 1; i <= Newn; i++)
{
Trie.insert(a[i]);
}
Trie.dfs(0);
while(m--)
{
int x;
scanf("%d",&x);
s ^= x;
printf("%d
",Trie.query());
}
return 0;
}