题意:给出n(<=3e5)个数字(<=3e5),现在对原序列的每个数字连续做m(<=3e5)次异或操作。每次把所有数字都抑或一个x。(第k次抑或是在第k-1次抑或的结果上进行)。每次操作之后求mex。mex定义为:序列中最小的 没有出现过的非负整数。比如mex[0,1,2,4,5]=3。mex[2,4,6,9]=0。
题解:首先,第一次要求把所有数字抑或x1,第二次要求吧所有数字继续抑或x2,就等价于把原数列抑或一个x1^x2,于是原数列不需要做改变,每次做查询就可以了。
考虑mex的二进制位,mex是*最小*的*抑或之后*没出现过的数字(从高位开始决定,如果高位可以为0就确定为0,否则确定为1,那么我们吧mex还原回去就是mex^x,mex^x这个数字原序列中不存在。那么也就是mex^x从高位开始决定,如果高位可以和x这一位相同,就确定为x的那一位,否则选不同于x那一位的数字)。把所有数字只考虑20位。mex最高位(第19位)选0不合法意味着:抑或完成之后,新数列出现了[0..2^19-1]范围内的所有数字。而这些数字共同的特点是 原数第19位和x相同(这样抑或之后才为0嘛)。而后边的[0..18]这19位中,新数可以实现任意的组合,那么原数必然也实现了所有的组合。如果我们把所有数字放到一个类似Trie的树上(字符集={0,1}),高位浅,低位深这样建,那么mex最高位不能是0,意味着从根出发,走(x的第19位)这个字符 达到的那个节点是一个满的树。这样如果我们让最高位=0了,我们继续往下走完所有20层,是无法得到一个“没有出现过的整数”的(最高位=0的所有数字都会出现在新数中)。因此只有在这种情况下,mex最高位被迫选1,然后我们继续同样的考虑第18位怎么选。
把思路总结以下就是:先建一个满的,总共有20层的二叉树,每个节点记一个full标记。对原数列所有数字进行二进制分解,插入到树中,把结束位置的full标记改成true。然后再build以下这个树,就是把非叶子结点的full标记更新一下。(如果两个孩子都是full,那么把我也改成full)然后得到一个询问X(=输入数据中 xi 的抑或前缀和)然后从最高位开始选,优先选和x相同的,如果选x相同的,到达的那个点是个满树,那就只能选和x不同的,同时确定了答案(mex)的这一位是1。进行20位的选择之后得到mex输出即可。
总的来说,代码很简单,但是写之前要想清楚才行。
依然采用结构体指针形式的Trie树。
Code:
#include<bits/stdc++.h>
using namespace std;
const int MAX = 3e5+100;
struct Node{
Node* nxt[2];
int deep;
bool full;
};
Node* create(int dep){
Node* n = (Node*)(malloc(sizeof (Node)));
memset(n->nxt,0,sizeof(n->nxt));
n->deep = dep;
n->full = false;
return n;
}
Node* root = create(-1);
void init(Node* nod){
if (nod->deep==19){
return;
}
nod->nxt[0] = create(nod->deep+1);
init(nod->nxt[0]);
nod->nxt[1] = create(nod->deep+1);
init(nod->nxt[1]);
}
void insert(int x){
Node* nod = root;
for (int i=19;i>=0;i--){
int id = (x&(1<<i))?1:0;
nod = nod->nxt[id];
}
nod->full = true;
}
void build(Node* nod){
if (nod->deep==19){
return;
}
build(nod->nxt[0]);
build(nod->nxt[1]);
nod->full = nod->nxt[0]->full&nod->nxt[1]->full;
}
int query(int x){
Node* nod = root;
int res =0;
for (int i=19;i>=0;i--){
int id = (x&(1<<i))?1:0;
if (nod->nxt[id]->full){
nod = nod->nxt[!id];
res+=(1<<i);
}else{
nod=nod->nxt[id];
}
}
return res;
}
int m,n;
int main(){
scanf("%d%d",&n,&m);
init(root);
for (int i=0;i<n;i++){
int temp;
scanf("%d",&temp);
insert(temp);
}
build(root);
// cout<<"YES"<<endl;
int K =0;
while (m--){
int temp;
scanf("%d",&temp);
K^=temp;
printf("%d\n",query(K));
}
return 0;
}