题目链接请戳这里http://www.codeforces.com/problemset/problem/706/D
题目大意:
有一个允许多元素重复的集合A,集合初始的时候只有0。
输入一个整数q(
1≤q≤200000
),代表q个操作,每个操作有三种情况:
+ x:向集合中增加数x(
1≤x≤109
)
- x:删除数x(确保x一定在集合中)
? x:求
max x⊕y,y∈A
题目分析:
一开始没有思路,通过百度发现涉及异或最大值的题都是将数据按从高到低位放在Trie树中,那么+和-操作就很好办了(Trie树就是按前缀保存的),?操作也好办,既然要求异或值最大,就从高到低往下搜索,使得与x相反的位尽可能多,一旦Trie树中存在与x对应位相反的前缀,就更新答案
ans+=1<<i
(其中i为位数)。
其实微软有一道机试题是跟子网段IP地址有关的,那道题是把IP地址的点分式拆成二进制形式,从高到低用Trie树维护,那么很显然Trie树的一个节点就是一个子网!,那道题中提到的admit和deny操作也就对应Trie树的添加前缀和删除前缀……
下面的ac代码可以用作Trie树的模板,将二叉树改为26叉树就可以适用于传统学习到的描述字母的Trie树。
#include <bits/stdc++.h>
using namespace std;
struct Trie {
Trie* next[2];
int cnt;
Trie() {
memset(next,0,sizeof(next));
cnt=0;
}
};
Trie* tr;
void insert(int n) {
Trie *p=tr;
for(int i=30;i>=0;i--) {
int j=(n&(1<<i))?1:0;
if(p->next[j]==NULL)
p->next[j] = new Trie();
p=p->next[j];
p->cnt++;
}
}
void del(int n) {
Trie *p=tr;
for(int i=30;i>=0;i--) {
int j=(n&(1<<i))?1:0;
p=p->next[j];
p->cnt--;
}
}
int query(int n) {
int ans=0;
Trie *p=tr;
for(int i=30;i>=0;i--) {
int j=(n&(1<<i))?0:1;
if(p->next[j] && p->next[j]->cnt) {
ans+=1<<i;
p=p->next[j];
}
else {
if(j==1)
p=p->next[0];
else
p=p->next[1];
}
}
return ans;
}
int main() {
int q;
tr = new Trie();
insert(0);
cin>>q;
while(q--) {
char c;
int x;
cin>>c>>x; //这个很慢!!!!
if(c=='+')
insert(x);
else if(c=='-')
del(x);
else
printf("%d\n", query(x));
}
}