1. 程式人生 > >【線段樹與01trie樹】woj 2645 hyc的xor/mex

【線段樹與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));
	}
}