zr#956 集合
分析
我们发现+1操作就是对于一段连续的1找到它前面最近的0
将这一整段每个数都异或1
我们可以在01trie上走将每一个合法点拿出来打上标记
然后最后依次交换所有标记点的左右儿子即可
我们继续考虑带异或的问题
我们发现我们可以存一下之前的总异或值
然后所有操作前把数跟这个数异或一下即可
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,trie[10000000][2],siz[10000000],cnt,all,d[10000000],res;
int ans[10000000],sum;
inline void ins(int x,int k){
int i,j,p=0;
for(i=0;i<30;i++){
int wh=(x&(1<<i))?1:0;
siz[p]+=k;
if(!trie[p][wh])trie[p][wh]=++cnt;
p=trie[p][wh];
}
siz[p]+=k;
}
inline void rev(){
int i,j,x=((1<<30)-1)^all,p=0;
res=0;
for(i=0;i<30;i++){
int wh=(x&(1<<i))?1:0;
d[++res]=p;
p=trie[p][wh];
if(!p)break;
}
for(i=1;i<=res;i++)swap(trie[d[i]][0],trie[d[i]][1]);
}
inline void dfs(int p,int x,int wh){
if(!siz[p])return;
if(!trie[p][0]&&!trie[p][1]){
for(int i=1;i<=siz[p];i++)ans[++sum]=x^all;
return;
}
if(trie[p][0])dfs(trie[p][0],x,wh+1);
if(trie[p][1])dfs(trie[p][1],x+(1<<(wh+1)),wh+1);
}
int main(){
int i,j,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
int x;
scanf("%d",&x);
ins(x,1);
}
for(i=1;i<=m;i++){
int x;
scanf("%d",&k);
if(k==1)scanf("%d",&x),ins(x^all,1);
else if(k==2)scanf("%d",&x),ins(x^all,-1);
else if(k==3)rev();
else scanf("%d",&x),all^=x;
}
dfs(0,0,-1);
sort(ans+1,ans+sum+1);
for(i=1;i<=sum;i++)printf("%d ",ans[i]);
puts("");
return 0;
}