D. Vasiliy’s Multiset
time limit per test4 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Author has gone out of the stories about Vasiliy, so here is just a formal task description.
You are given q queries and a multiset A, initially containing only integer 0. There are three types of queries:
“+ x” — add integer x to multiset A.
“- x” — erase one occurrence of integer x from multiset A. It’s guaranteed that at least one x is present in the multiset A before this query.
“? x” — you are given integer x and need to compute the value , i.e. the maximum value of bitwise exclusive OR (also know as XOR) of integer x and some integer y from the multiset A.
Multiset is a set, where equal elements are allowed.
Input
The first line of the input contains a single integer q (1 ≤ q ≤ 200 000) — the number of queries Vasiliy has to perform.
Each of the following q lines of the input contains one of three characters ‘+’, ‘-’ or ‘?’ and an integer xi (1 ≤ xi ≤ 109). It’s guaranteed that there is at least one query of the third type.
Note, that the integer 0 will always be present in the set A.
Output
For each query of the type ‘?’ print one integer — the maximum value of bitwise exclusive OR (XOR) of integer xi and some integer from the multiset A.
Example
inputCopy
10
+ 8
+ 9
+ 11
+ 6
+ 1
? 3
- 8
? 3
? 8
? 11
outputCopy
11
10
14
13
Note
After first five operations multiset A contains integers 0, 8, 9, 11, 6 and 1.
The answer for the sixth query is integer — maximum among integers , , , and .
题意:给出q个操作,若操作带+号是向集合中插入这个数,带-号表示删除这个数,带问号表示,查询集合中的数与输入的数异或最大值,输出这个异或结果,注意题目要求0一直在树的根节点,也就是说,输入的数可以与集合中的任意一个值异或,也可以不异或(即与0异或)得到最大值。
典型的01字典树,直接+号建树,根据-号删除相应节点,删除方法即对树上的节点走过的进行计数,那么就是cnt++和cnt–,删除时直接对路径上每个节点计数减一,注意不用动节点上创建的编号,因为这条编号可能是其他数的路径,删除了一个数不代表整个子树在此被截断。
最后在查询时,既要存在这个被查询的节点,又要计数不为0,那么搜索到的就是经过删除的树,否则只像之前一样判断是否存在节点,会忽略删除的标记
#include<bits/stdc++.h>///01字典树
#define LL long long
#define M(a,b) memset(a,b,sizeof a)
using namespace std;
const int maxn=1e5+7;
int tre[maxn<<5][2];
int cnt[maxn<<5];///标记节点走过次数,就不用关心原节点链接向哪个标号了
int vis[maxn<<5];
int q,tot=0;
char flag[3];
void insert(int a,int rt,int add)
{
for(int i=31; i>=0; i--)
{
int x=(a>>i)&1;
if(!tre[rt][x])
{
tre[rt][x]=++tot;
M(tre[tre[rt][x]],0);
}
rt=tre[rt][x];
cnt[rt]+=add;///删除和插入就是对某个节点的经过次数计数
}
vis[rt]=a;
}
int finds(int a,int rt)
{
for(int i=31;i>=0;i--)
{
int x=(a>>i)&1;
if(tre[rt][!x]&&cnt[tre[rt][!x]])rt=tre[rt][!x];///必须同时符合两个条件才能往这条子树上走
else rt=tre[rt][x];
}
return vis[rt];
}
int main()
{
int tmp,rt=++tot;
M(tre[rt],0);
M(cnt,0);
M(vis,0);
scanf("%d",&q);
while(q--)
{
scanf("%s",flag);
scanf("%d",&tmp);
if(flag[0]=='+') insert(tmp,rt,1);
else if(flag[0]=='-') insert(tmp,rt,-1);
else printf("%d
",max(tmp,tmp^finds(tmp,rt)));///因为0一直在根部,因此必须检查一下这个数异或0(输出自己)和异或集合中某个数来取最大值
}
return 0;
}