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

 

posted @ 2019-09-06 08:36  水题收割者  阅读(179)  评论(0编辑  收藏  举报