Codeforces Round #367 (Div. 2) D. Vasiliy's Multiset (0/1-Trie树)

Vasiliy's Multiset

题目链接:

http://codeforces.com/contest/706/problem/D

Description

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.

Examples

input
10
+ 8
+ 9
+ 11
+ 6
+ 1
? 3
- 8
? 3
? 8
? 11
output
11
10
14
13

Source


Codeforces Round #367 (Div. 2)


题意:


维护一个multiset:
操作1:插入一个数字
操作2:删除指定数字
操作3:给出一个x,求集合中的一个元素y,使得 x XOR y 最大.


题解:


由于异或前后的值不能保持单调性,所以排序和线段树都不好处理.
这里需要用一个 0/1-Trie树 来维护.
对于集合中的数用二叉树中的结点来表示其二进制位.
①插入:开辟一些新的树结点.
②删除:找到对应结点赋成NULL. 删除时有几点需要注意:
集合允许重复,所以可以记录每个数出现的次数,当集合中仅有一个x时才删除.
把最底端的叶节点删除后,要递归更新其父结点的状态,否则在匹配时可能会进入不存在的结点.
③匹配:先把x按位取反,从高位到低位在二叉树中匹配x,按能匹配时尽量匹配的原则往下走. 这样可以保证结果从高位到低位有尽量多的位不相同,即异或结果最大.
注意:初始时集合中有个0,一开始忘了这个,RE了2发.


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <vector>
#include <list>
#define LL long long
#define eps 1e-8
#define maxn 101000
#define mod 100000007
#define inf 0x3f3f3f3f
#define mid(a,b) ((a+b)>>1)
#define IN freopen("in.txt","r",stdin);
using namespace std;

class Trie
{
    public:
    Trie *next[2];
    Trie() {
        memset(next,NULL,sizeof(next));
    }
};

Trie *root;

void Insert(LL n)
{
    Trie *p = root;
    for(int i=31;i>=0;i--)
    {
        int id = (n >> i) & 1;
        if(p->next[id] == NULL)
             p->next[id] = new Trie();
        p = p->next[id];
    }
}


int Delete(Trie *p, LL n, int i)
{
    if(i < 0) return 1;
    int id = (n >> i) & 1;
    int ret = Delete(p->next[id], n, i-1);
    if(ret) p->next[id] = NULL;
    if(p->next[0] == NULL && p->next[1] == NULL)
        return 1;
    return 0;
}

LL Match(LL m)
{
    m = ~m;
    LL ans = 0;
    Trie *p = root;
    for(int i=31;i>=0;i--) {
        ans <<= 1;
        int bit = (m >> i) & 1;
        if(bit) {
            if(p->next[1]) {
                p = p->next[1];
                ans++;
            }
            else {
                p = p->next[0];
            }
        }
        else {
            if(p->next[0]) {
                p = p->next[0];
            }
            else {
                p = p->next[1];
                ans++;
            }
        }
    }
    return ans;
}

map<LL, int> occ;

int main(int argc, char const *argv[])
{
    //IN;

    int q;
    while(scanf("%d", &q) != EOF)
    {
        root = new Trie();
        occ.clear();
        Insert(0LL);
        occ[0] = 1;
        while(q--)
        {
            getchar();
            char op; LL val;
            op = getchar();
            scanf("%I64d", &val);

            if(op == '+') {
                if(!occ[val]) {
                    occ[val] = 1;
                    Insert(val);
                }
                else occ[val]++;
            }
            else if(op == '-') {
                if(occ[val] == 1) Delete(root, val, 31);
                occ[val]--;
            } else {
                printf("%I64d\n", Match(val)^val);
            }
        }
    }

    return 0;
}

转载于:https://www.cnblogs.com/Sunshine-tcf/p/5793604.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值