CF842D题解
题目描述
给你一个长度为 \(n(n\leq 3e5)\) 整数数列,对于每一次询问,我们将序列中每一个元素都异或上一个数字 \(x\) 然后查询序列 \(mex\) ,其中 \(mex\) 定义为最小没有出现的自然数。
分析
我们先想想如何快速求一个序列的 \(mex\) ,一种可能很常见的方法是建立一颗权值线段树,然后对于每一个值域,如果左儿子中数的数量等于区间长度,那么说明答案在右子树中,否则就在左子树中。
但是这种做法不能很好的处理异或,那我们怎么办呢?处理异或的问题,我们通常采用的是01trie,那么我们如何使用01trie呢?
经过我们画图观察可以发现如果出现了一段连续的数字,01trie上对应的子树一定是满的
然后我们和上面不带修改的方法类似,再插入的过程中对于每一个01trie上的节点,我们记录一下它的子树大小,如果它是满的,说明它的 \(mex\) 在另外一边。
在考虑如何处理异或的条件,在01trie上就是相当于交换两颗子树,我们只需要加上一点小小的判断就可以了。
#include
using namespace std;
const int N=3e5+10;
int n,m,tr[N*30][2],tot,sz[N*30];
inline void Ins(int cur,int weis,int x){
if(weis<0) {sz[cur]=1;return;}
int ch=x>>weis&1;
if(tr[cur][ch]==0) tr[cur][ch]=++tot;
Ins(tr[cur][ch],weis-1,x);
sz[cur]=sz[tr[cur][0]]+sz[tr[cur][1]];
}
inline int Qry(int cur,int axor){
int res=0;
for(int weis=25;weis>=0;weis--){
int ch=axor>>weis&1;
if(sz[tr[cur][ch]]==(1<<(weis))) {
res|=(1<<(weis));
cur=tr[cur][ch^1];
} else {
cur=tr[cur][ch];
}
if(cur==0) return res;
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;tot=1;
for(int i=1;i<=n;i++){
int x;cin>>x;Ins(1,25,x);
}
int axor=0;
for(int i=1;i<=m;i++){
int x;cin>>x;axor^=x;
cout<