题目链接:
https://vjudge.net/problem/CodeForces-706D
http://codeforces.com/contest/706/status/D
题意:
n次操作,三种操作,给定一个空集,插入一个数,删除一个数,查询给定数与集合中某一数的最大异或值;
分析:
比较坑的是题目说了0始终存在于集合中,想一想这种情况给你一个数x,结果它和集合中的任意非零数的异或都小于它本身x,而0与x的异或是x,刚好是最大的,这样就必须一开始插入0;开始没看见题目这个条件,而且没考虑到0的这种情况,所以一直wa,而且我把十进制转化位32位存进去的时候,和转化位31、30位的结果截然不同,不知道什么原因;
关于删除一个数,当你把一个十进制数转化位二进制存入进去的时候,每个二进制都有一个编号,每当你存入一个数,那么就会经过那个数对应二进制每一位一次,比如12与10分别是,1100和1011,那么插入的时候左边第一个1以及之前的零都是一样的(共用),在一起是经过两次,在第一个1之后会产生分支;那么你用一个数组记录经过这个数每一个二进制位的次数,当你要删除的时候,把次数减1即可,查询的时候,优先考虑相反(0就走1,1就走0)并且判断这条分支经过的次数是不是大于0,同时满足就可以走;
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
const int inf=0x7f7f7f7f;
const int maxn=1e1+50;
const int N=5e6+50;
typedef long long ll;
typedef struct{
ll u,v,next,w;
}Edge;
Edge e[N];
int cnt,head[N];
inline void add(int u,int v){
e[cnt].u=u;
e[cnt].v=v;
//e[cnt].w=w;
// e[cnt].f=f;
e[cnt].next=head[u];
head[u]=cnt++;
// e[cnt].u=v;
// e[cnt].v=u;
// e[cnt].w=0;
// e[cnt].f=-f;
// e[cnt].next=head[v];
// head[v]=cnt++;
}
inline int read()
{
int x = 0;
int f = 1;
char c = getchar();
while (c<'0' || c>'9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0'&&c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x*f;
}
int q,t,num=1,trie[N][2],val[N],cot[N];
char c;
void insert(int x){
int p=1;
for(int i=31;i>=0;i--){
int r=(x>>i)&1;
if(i==31&&x==8)
cout<<r<<endl;
if(!trie[p][r]){
trie[p][r]=++num;
}
p=trie[p][r];
cot[p]++;
}
val[p]=x;
}
void delet(int x){
int p=1;
for(int i=31;i>=0;i--){
int r=(x>>i)&1;
p=trie[p][r];
cot[p]--;
}
}
int query(int x){
int p=1,ans;
for(int i=31;i>=0;i--){
int r=(x>>i)&1;
if(cot[trie[p][r^1]])p=trie[p][r^1];
else p=trie[p][r];
}
//cout<<val[p]<<endl;
return val[p]^x;
}
int main() {
cin>>t;
insert(0);
while(t--){
getchar();
cin>>c>>q;
if(c=='+'){
insert(q);
}
else if(c=='-'){
delet(q);
}
else if(c=='?'){
cout<<query(q)<<endl;
}
}
return 0;
}
(仅供个人理解)