codeforces 706D(字典树)

题目链接:http://codeforces.com/problemset/problem/706/D

题意:
给出q个操作,操作有三种类型。
①往集合添加一个数(+ x)
②删除集合里的某个数(- x)
③询问集合里某个数与给定的数异或的最大值(? x)

分析:
字典树从根节点往叶节点依次存取二进制数的高位到低位,寻找异或最大值时,沿着与当前位相反的路径走。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<map>
#include<vector>
#include<set>
using namespace std;
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define INF ((1<<31)-1)
#define in(x) scanf("%d", &x)
#define lin(x) scanf("%I64d", &x)
#define out(x) printf("%d\n", x)
#define rep(i,a,b) for(int i=a; i<=(b); i++)
#define ll __int64
#define mem(a,b) memset(a, b, sizeof(a))
#define bug(x) cout<<#x<<" = "<<x<<";   "
#define N 205
#define nn printf("\n")

/**
数据范围1e9<2^30,最多从0位存到第30位
*/

struct A
{
    int tim, son[2];
    ///tim:该节点出现几次
    ///son[i]:儿子i在树中的编号

    void set0()
    {
        tim=0;
        mem(son,0);///0表示不存在
    }
}trie[8000000];

int ind;///树的编号,trie数组的下标
int pow2[35];    ///pow2=1,2,4,8,16...下标从0开始
///pow2[i]表示1后面有i个0

void init()
{
    ind=1;///根节点
    trie[1].set0();

    pow2[0]=1;
    rep(i,1,30)
        pow2[i]=pow2[i-1]*2;
}

//删除数字num
void del(int num)
{
    int root=1, x;
    for(int i=30; i>=0; i--)
    {
        x=(num&pow2[i])?1:0;///取出该位
        int fa=root;
        root=trie[root].son[x];///得到傻儿子编号
        trie[root].tim--;
        if(trie[root].tim==0)///该儿子出现次数为0
        {
            trie[fa].son[x]=0;///父节点删掉该儿子
            break;///删掉该儿子等于删掉该儿子的所有子节点
            ///因为只能通过该儿子的编号访问其子节点,然而儿子编号没了
            ///下一次赋给该儿子的编号又是一个新值
        }
    }
}

//添加数字num
void add(int num)
{
    int root=1, x;

    ///分解num的每一位,从高到低
    for(int i=30; i>=0; i--)
    {
        x=(num&pow2[i])?1:0;///取出该位
        if(trie[root].son[x]==0)///傻儿子未出现过
        {
            ind++;
            trie[root].son[x]=ind;///儿子编号赋为ind
            trie[ind].set0();
        }
        root=trie[root].son[x];
        trie[root].tim++;
    }
}

//询问与num异或之后的最大数
int query(int num)
{
    int root=1, x, ans=0;
    for(int i=30; i>=0; i--)
    {
        x=(num&pow2[i])?1:0;///取出该位
        int ez=trie[root].son[x^1];///与该位相反的儿子编号
        if(ez && trie[ez].tim>0)
        {
            ans+=pow2[i];
            root=ez;
        }
        else
            root=trie[root].son[x];
    }
    return ans;
}

int main()
{
    int q;
    in(q);
    init();
    add(0);
    while(q--)
    {
        char s[5];
        int k;
        scanf("%s %d",s,&k);
        if(s[0]=='+')
            add(k);
        else if(s[0]=='-')
            del(k);
        else
            printf("%d\n", query(k));
    }

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值