Codeforces Round #367 (Div. 2) D. Vasiliy's Multiset(带删除的01字典树)
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\n",max(tmp,tmp^finds(tmp,rt)));///因为0一直在根部,因此必须检查一下这个数异或0(输出自己)和异或集合中某个数来取最大值
}
return 0;
}