01字典树,数据结构
给出长度为 n 的非负整数序列,求该序列异或 x 以后的 mex 值。
求mex可以用trie来搞。把整个序列的trie建出来后,假如要全局异或一个x,若x的从高到低第i位为1,则trie的第i层的所有节点都要翻转左右儿子。 知道了这点后,我们就可以通过打标记实现翻转来快速异或一个值,而不需要实际进行异或。
因为所有数都在300000内,那么01字典树开20层就够。标记什么的类似线段树,边查询边pushdown,如果这一层对应的位是1,那么交换左右儿子。每一个节点存一个是否儿子满了的标志,查询时如果左儿子没满,mex值肯定在左子树;要是满了,就进入右子树。
注意运算符优先级
#include<cstdio> #include<cstring> #include<algorithm> #define M(a,b) memset(a,b,sizeof(a)) #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 using namespace std; const int MAXN=300007; const int bit=20; typedef long long LL; struct Trie { int s; int ch[2]; int rev; Trie() { s=ch[0]=ch[1]=rev=0; } }trie[MAXN*bit]; int sz=0; void build() { M(trie, 0); } void pushdown(int rt, int lev) { if(lev<=0||trie[rt].rev==0) return; int tmp=trie[rt].rev;trie[rt].rev=0; int ls=trie[rt].ch[0], rs=trie[rt].ch[1]; trie[ls].rev^=tmp, trie[rs].rev^=tmp; if(tmp&(1<<(lev-1))) swap(trie[ls].ch[0], trie[ls].ch[1]), swap(trie[rs].ch[0], trie[rs].ch[1]); } void insert(int rt, int lev, int num) { if(lev==-1) { trie[rt].s=1;return; } int k=(num&(1<<lev))>>lev; if(!trie[rt].ch[k]) trie[rt].ch[k]=++sz; insert(trie[rt].ch[k], lev-1, num); trie[rt].s=trie[trie[rt].ch[0]].s&trie[trie[rt].ch[1]].s; } int query(int rt, int lev) { if(lev==-1) return 0; pushdown(rt, lev); if(!trie[trie[rt].ch[0]].s) return query(trie[rt].ch[0], lev-1); else return query(trie[rt].ch[1], lev-1)+(1<<lev); } int main() { int n, m; while(scanf("%d%d", &n, &m)==2) { sz=1;build(); for(int i=1;i<=n;i++) { int tmp;scanf("%d", &tmp); insert(1, bit, tmp); } for(int i=1;i<=m;i++) { int tmp;scanf("%d", &tmp); trie[1].rev^=tmp; if(tmp&(1<<bit)) swap(trie[1].ch[0], trie[1].ch[1]); printf("%d\n", query(1, bit)); } } return 0; }