题目大意:
最开始的时候有一个集合,集合里面只有一个元素0,现在有q次操作,操作分为3种:
+ x: 表示向集合中添加一个元素x
- x:表示删除集合中值为x的一个元素
? x:表示查询集合中与x异或的最大值为多少
解题思路:
乱搞。
可以用一个multiset来维护这个集合。这样前两个操作就是非常简单的了,关键在于最后一个操作。
对于异或来说,比如说如果给你一个数11,另外一个数是30 bit之内的数,他们的异或最大是多少?答案是1073741823。
怎么得到的呢?
先把11转换成二进制:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0
那么与11异或之后获得最大值就是
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1也就是1073741823
而获得这个最大值的与11异或的那个数就是1073741812
所以我们可以维护一个字典树来获得这个结果,字典树保存的就是每个数的二进制串,然后每次查询的时候能取相反就取相反。
不过我们是用multiset来维护这个集合,那么其实对于第三个操作也可以按照上面的思想来做。
对于任意的一个x,每次获取它的一个位上的值,然后取反,将这个值加入到tmp的这个位上,然后进行判断,在multiset里面查询,如果在tmp到tmp+(1<<i)这个区间内有这个值,那么说明这个值可取,如果不存在,则不可取,然后把这个值从tmp的位上取下即可。具体看代码。
代码:
#include <set>
#include <iostream>
#include <algorithm>
using namespace std;
//#define DEBUG 1
multiset<int> s;
multiset<int>::iterator it;
int bit(int x, int i) {
return (x >> i) & 1;
}
int check(int a, int b) {
it = s.lower_bound(a);
if (it == s.end()) return 0;
if ((*it) < b) return 1;
else return 0;
}
int solve(int x) {
int tmp, ans = 0;
for (int i = 30; i >= 0; --i) {
tmp = bit(x, i);
if (!tmp) ans |= (1LL << i);
if (!check(ans, ans + (1LL << i))) ans ^= (1LL << i);
}
return (ans ^ x);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
char op;
int q, x;
s.insert(0);
cin >> q;
while (q--) {
cin >> op >> x;
if (op == '+') {
s.insert(x);
} else if (op == '-') {
it = s.find(x);
s.erase(it);
} else {
cout << solve(x) << endl;
}
}
#ifdef DEBUG
system("pause");
#endif // DEBUG
return 0;
}