【35.20%】【CF 706D】Vasiliy's Multiset

time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard 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:

  1. "+ x" — add integer x to multiset A.
  2. "- 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.
  3. "? 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
input
10
+ 8
+ 9
+ 11
+ 6
+ 1
? 3
- 8
? 3
? 8
? 11
output
11
10
14
13
Note

After first five operations multiset A contains integers 089116 and 1.

The answer for the sixth query is integer  — maximum among integers  and .

【题解】

如果题目没看懂就看一下上面那个note的样例解释嘛。应该知道是什么意思了吧。

然后我把样例的前6个操作数的二进制列举一下。

如果不足4位就前面补0

1000
1001
1011
0110
0001


询问0011

思路是这样的

把前面5个数字加入字典树中。

然后从根节点开始。看到询问的二进制第一位是0.

0的话xor什么最好呢?当然是1.

那就看看从根节点往下有没有一个1有就往下走,没有的话就走0(0是一直存在的。所以肯定可以走);然后二进制的第二位也以此类推。

最后我们走到底的肯定是最大值。

这里涉及到一个原理就是

(二进制)

1000 > 0111

即2^x > 2^(x-1)+2^(x-2) + ...+ 2^0

实际上左边那个等于右边加1.

节点加入和删除的话

给每个节点都设置一个值。走到这个点就增加一下这个节点的值。然后要删除的时候仍旧走一遍这个路。然后递减路上走过的节点的值。

这3个操作其实很像的。

然后10^9 二进制的长度为30

【代码】

#include <cstdio>

const int MAX_SIZE = 7000000;

int q, v[MAX_SIZE][2] = { 0 }, totn = 0, num[MAX_SIZE] = { 0 };
int a[40];

//v[t][0],v[t][1]分别表示t的左右儿子,左儿子代表0

void add(int x)
{
	int temp = 0, temp1 = x;
	a[0] = 30;
	while (temp1 > 0) //从后往左加入二进制各个位
	{
		a[a[0]] = temp1 & 1;
		temp1 = temp1 >> 1;
		a[0]--;
	}
	for (int i = 1; i <= a[0]; i++)//1..a[0]是补0
	{
		if (v[temp][0] == 0)
			v[temp][0] = ++totn;
		temp = v[temp][0];
		num[temp]++;
	}
	for (int i = a[0] + 1; i <= 30; i++)//a[0]+1..30才是这个数的二进制
	{
		if (v[temp][a[i]] == 0)
			v[temp][a[i]] = ++totn;
		temp = v[temp][a[i]];
		num[temp]++;
	}
}

void de_lete(int x)
{
	int temp = 0, temp1 = x;
	a[0] = 30;
	while (temp1 > 0)
	{
		a[a[0]] = temp1 & 1;
		temp1 = temp1 >> 1;
		a[0]--;
	}
	for (int i = 1; i <= a[0]; i++)
	{
		temp = v[temp][0];
		num[temp]--;
	}
	for (int i = a[0] + 1; i <= 30; i++)
	{
		temp = v[temp][a[i]];
		num[temp]--;
	}
}

int query(int x)
{
	int temp = 0, temp1 = x;
	a[0] = 30;
	while (temp1 > 0)
	{
		a[a[0]] = temp1 & 1;
		temp1 = temp1 >> 1;
		a[0]--;
	}
	int leijia = 0;
	for (int i = 1; i <= a[0]; i++)
		if (v[temp][1] != -1 && num[v[temp][1]] > 0)
		{
			temp = v[temp][1];
			leijia += 1 << (30 - i); //代表2^(30-i)
		}
		else
			temp = v[temp][0];
	for (int i = a[0] + 1; i <= 30; i++)
		if (v[temp][1 - a[i]] != -1 && num[v[temp][1 - a[i]]] > 0)
		{
			temp = v[temp][1 - a[i]];
			leijia += 1 << (30 - i);
		}
		else
			temp = v[temp][a[i]];
	return leijia;
}

void input_data()
{
	scanf("%d", &q);
	for (int i = 1; i <= q; i++)
	{
		char op[5];
		int x;
		scanf("%s%d", op, &x);
		if (op[0] == '+')
			add(x);
		else
			if (op[0] == '-')
				de_lete(x);
			else
				printf("%d\n", query(x));
	}
}

int main()
{
	//freopen("F:\\rush.txt", "r", stdin);
	add(0);
	input_data();
	return 0;
}


posted @ 2017-10-06 19:23  AWCXV  阅读(145)  评论(0编辑  收藏  举报