题目大意:
最开始的时候有一个集合,集合里面只有一个元素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; }