描述
NOIP2017就要来了,备战太累,不如做做hyc的新题?
找回自信吧!
一句话题意:n个数,m个操作
操作具体来讲分两步
1.读入x,把n个数全部xor上x
2.询问当前n个数的mex
意味着每次操作后你都需要输出一次
(注意:是mex,即集合内未出现过的最小非负整数
举2个例子 mex(4,33,0,1,1,5)=2 mex(1,2,3)=0)
输入
第一行两个整数n,m 意义同题面(1 ≤ n, m ≤ 3 * 10^5)
第二行 n个数 ai (0 ≤ ai ≤ 3 * 10^5)
接下来 m 行
每行一个整数 x
表示将所有数xor上x (0 ≤ x ≤ 3 * 10^5).
输出
一共m行
每行表示当前n个数的xor
样例输入[复制]
5 4
0 1 5 6 7
1
1
4
5
样例输出[复制]
2
2
0
2
提示
30%数据n,m<=1000
100%数据同“输入”
标签
mogician原创
异或操作经常都跟trie树有关。异或具有结合律,那么可以把每次异或的值用一个数组记录下来,最后再跟原序列去异或。
首先建树,把每个结点的值域区间记录一下。
然后原序列用一个权值线段树维护一下。
然后把原来的线段树看做是trie树,左儿子就是0,右儿子就是1.
然后是query操作。大概是二进制位的贪心。先一直往左走。然后如果某一位需要异或1,就往反方向走,因为相当于是把左右子树交换了一下。如果需要异或0,就不变。如果一个结点所代表的值域中的数都出现过。。【tr[tmp].cnt==tr[tmp].r-tr[tmp].l+1】就给答案加上2^depth,然后调头走另一条路。
具体看代码吧。%ldx
#include<bits/stdc++.h>
using namespace std;
const int inf=(1<<19)-1;
inline void read(int &x){
x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}
struct trnode{
int l,r,cnt;
}tr[1211111];
int n,m,x;
int s[20];
inline void pushup(int root){ tr[root].cnt=tr[root<<1].cnt+tr[root<<1|1].cnt;}
inline void build(int root,int l,int r){
tr[root].l=l,tr[root].r=r;
if(l==r){
tr[root].cnt=0;
return;
}
int mid=(tr[root].l+tr[root].r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
}
void update(int root,int x){
if(tr[root].l==tr[root].r){
tr[root].cnt=1;
return;
}
int mid=tr[root].l+tr[root].r >>1;
if(x<=mid) update(root<<1,x);
else update(root<<1|1,x);
pushup(root);
}
int query(int root,int depth){
if(tr[root].l==tr[root].r)
return 0;
int tmp=(root<<1)|s[depth];
if(tr[tmp].cnt==tr[tmp].r-tr[tmp].l+1) return query(tmp^1,depth-1)+(1<<depth);
return query(tmp,depth-1);
}
int main(){
read(n),read(m);
build(1,0,inf);
while(n--){
read(x);
update(1,x);
}
while(m--){
read(x);
for(int i=1;i<=19;++i)
if(x&(1<<i))
s[i]^=1;
printf("%d\n",query(1,18));
}
}