zoukankan      html  css  js  c++  java
  • 【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
    ", query(x));
    	}
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    	add(0);
    	input_data();
    	return 0;
    }


  • 相关阅读:
    第五次作业
    第四次作业
    第三次作业
    第二次作业
    2013551822第一次作业
    第八次作业
    第七次作业
    第六次作业
    第五次作业
    第四次作业
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632257.html
Copyright © 2011-2022 走看看