[ZROJ-956]集合 Solution

給你一個序列,請完成以下操作:

  • 插入一個數xx
  • 刪除一個數xx
  • 所有數+1+1,對2302^{30}取模。
  • 所有數異或上xx
    思考一下加法的實質。
    把一個數拆成低位到高位的形式,a1 a2 a3 a4a_1~a_2~a_3~a_4……,那麼我們實際上就是要找到一個最小的ii,使得a1=1,a2=1ai=1a_1=1,a_2=1……a_i=1,然後把這些數全部取反,讓ai+1=1a_{i+1}=1,這就完成了一個加法。如果說i=30i=30,那就1301-30取反,3131不用考慮,順便完成了取模。
    至於異或,我們就只需要搞一個異或標記就可以,完全不用考慮。
    那麼插入和刪除呢?TrieTrie?沒錯。怎麼搞加法?直接記一個數爲(1<<30)1(1<<30)-1,讓這個數異或上當前異或標記,然後在TrieTrie上面檢索它,走到的點把左右兒子分別交換,這樣就完成了加法。
    code:code:
#include <bits/stdc++.h>
#define regi register int
int n,m,tot,cnt,Xor;
int temp[10000001];
int A[10000001];
struct Trie{
	int end;
	int s[2];
}t[10000001];
inline int read(){
	int r=0,w=0,c;
	for(;!isdigit(c=getchar());r=c);
	for(w=c^48;isdigit(c=getchar());
	w=w*10+(c^48));
	return r^45?w:-w;
}
inline void insert(int x){
	int now=0;
	for(regi j=0;j<=29;++j){
		int to=(x>>j)&1;
		if(!t[now].s[to])
		  t[now].s[to]=++tot;
		now=t[now].s[to];
	}
	t[now].end++;
}
inline void zyinsert(int x){
	int now=0;
	for(regi j=0;j<=29;++j){
		int to=(x>>j)&1;
		now=t[now].s[to];
	}
	t[now].end--;
}
inline void rotate(int x){
	int now=0;
	int tail=0;
	for(regi i=0;i<=29;++i){
		temp[++tail]=now;
		int to=(x>>i)&1;
		now=t[now].s[to];
		if(!now)
		  break;
	}
	for(regi i=1;i<=tail;++i)
	  std::swap(t[temp[i]].s[0],t[temp[i]].s[1]);
}
void Put(int x,int val,int cs){
	if(cs==30){
		for(regi i=1;i<=t[x].end;++i)
		  A[++cnt]=val^Xor;
	}
	if(t[x].s[0])
	  Put(t[x].s[0],val,cs+1);
	if(t[x].s[1])
	  Put(t[x].s[1],val+(1<<cs),cs+1);
}
main(){
	n=read(),m=read();
	for(regi i=1,x;i<=n;++i){
		x=read();
		insert(x);
	}
	for(regi i=1,op,x;i<=m;++i){
		op=read();
	  op!=3?x=read():0;
	  if(op==1)
	  	insert(x^Xor);
	  if(op==2)
	  	zyinsert(x^Xor);
	  if(op==3)
	    rotate((((1<<30)-1)^Xor));
	  if(op==4)
	    Xor^=x;
	}
	Put(0,0,0);
	std::sort(A+1,A+cnt+1);
	for(regi i=1;i<=cnt;++i)
	  printf("%d ",A[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章