【线段树与01trie树】woj 2645 hyc的xor/mex

描述

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));
	}
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/83043351
今日推荐