题目链接:Vasiliy's Multiset
题意:这里有一个set容器,有三种操作,+ num, - num, ? num,分别代表往容器里加上num,或者拿走num,或着从容器里找一个数temp使得temp^num的值最大。输出这个最大值。
思路:对于XOR操作,一般都要拆位考虑,拆完之后用Trie或者线段树维护,然后,这个题把每个数的二进制30位(前面不够的用0补全)插入Trie,查询的时候,对于每一位先尝试往相反的方向走,因为异或 只要越高位
相反值越大,然后再尝试往相同的方向走。最后如果没有找到这个数的话,ans即为该数字本身,因为0一直在容器里。
tree[maxn][2],maxn应为n*30,因为一个数字占30个节点,总结点数不会超过n*30.
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #define maxn 200010*30 using namespace std; int tree[maxn][2]; // Trie int cnt[maxn]; // 每个节点的经过次数 int val[maxn]; //以每个节点为终点的数字 int tot; // 每个节点的编号 int num; int now[30]; // 保存每个数字的30位 void update(int x, int type) { int rt = 0; // 插入当前数字时的地址 num = 0; int t = x; memset(now, 0, sizeof(now)); while(t) { now[num++] = t%2; t /= 2; } for (int i=29; i>=0; --i) { int temp = now[i]; cnt[rt] += type; if (!tree[rt][temp]) { tree[rt][temp] = tot++; } rt = tree[rt][temp]; } cnt[rt] += type; val[rt] = x; } int query(int x) { int t = x; int rt = 0; num = 0; memset(now, 0, sizeof(now)); while(t) { now[num++] = t%2; t /= 2; } int ans = 0; for (int i=29; i>=0; --i) { int temp = now[i]; if ((tree[rt][temp^1]) && cnt[tree[rt][temp^1]]) { rt = tree[rt][temp^1]; } else if ((tree[rt][temp]) && cnt[tree[rt][temp]]) { rt = tree[rt][temp]; } else { ans = -1; break; } } if (ans != -1) { ans = max(val[rt]^x, x); } else ans = x; return ans; } int main() { // freopen("in.cpp", "r", stdin); int n; while(~scanf("%d", &n)) { tot = 1; memset(tree, 0, sizeof(tree)); memset(cnt, 0, sizeof(cnt)); memset(val, -1, sizeof(val)); for (int i=0; i<n; ++i) { char op; int temp; getchar(); scanf("%c%d", &op, &temp); //cout << op << "==" << temp << endl; if (op == '+') { update(temp, 1); }else if (op == '-') { update(temp, -1); }else { int ans = query(temp); printf("%d\n", ans); } } } return 0; }