0-1字典树

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5536

题意:给你n个数,要找三个不同的数a[i],a[j],a[k]使(a[i]+a[j])^a[k]最大,求最大值

分析:对于异或的最大值问题,一般考虑用01字典树,先把这n个数插入字典树中,然后直接枚举a[i]+a[j],并从字典树中暂时删除a[i],a[j],在字典树中直接找与(a[i]+a[j])异或最大的数,并返回最大值,最后把这两个数从字典树中还原

 

Ac code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+5;///trie[i][j] 表示编号为i的节点的第j个儿子
ll trie[32*maxn][2];///注意数组要开32*maxn
ll val[32*maxn];
ll a[maxn];
int tot;
int num[32*maxn];
void init(){
    memset(trie,0,sizeof trie);
    memset(val,0,sizeof val);
    memset(num,0,sizeof num);
    tot=0;///用于对节点编号
}
void Insert(ll d)///插入节点
{
    int rt=0;
    for(int i=32; i>=0; --i)
    {
        int id=(d>>i)&1;
        if(!trie[rt][id]) trie[rt][id]=++tot;///如果无该节点则新建
        rt=trie[rt][id];///往下面走
        num[rt]++;///记录每个节点出现的次数
    }
    val[rt]=d;///叶子节点的val数组为该数的值,其余非叶子为0
}
ll query(ll d)///询问与d异或的最大数并返回最大值
{
    int rt=0;
    for(int i=32; i>=0; --i)
    {
        int id=(d>>i)&1;
        if(trie[rt][id^1]&&num[trie[rt][id^1]]) rt=trie[rt][id^1];
        else rt=trie[rt][id];
    }
    return d^val[rt];
}
void update(ll a,int b)///用来间接删除节点,与num数组配合
{
    int rt=0;
    for(int i=32;i>=0;--i){
        int id=(a>>i)&1;
        rt=trie[rt][id];
        num[rt]+=b;
    }
}
int main()
{
    int n,t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            Insert(a[i]);
        }
        ll _max=0;
        for(int i=1; i<=n; i++)
            for(int j=i+1; j<=n; j++)
            {
                update(a[i],-1),update(a[j],-1);///先删除这两个数在字典树中的所有节点
                _max=max(_max,query(a[i]+a[j]));
                update(a[i],1),update(a[j],1);///后还原
            }
        printf("%lld\n",_max);
    }
    return 0;
}

 

 

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

题意:给你三种操作

1、+ x 表示向集合里添加x

2、 - x 表示去除集合里的x,但保证在次之前集合里有x

3、?x 表示x与集合里的数异或的最大值是多少?

分析:同上。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+1;
ll trie[32*maxn][2];
ll val[32*maxn];
int num[32*maxn];
int tot;
void init()
{
    memset(trie,0,sizeof trie);
    memset(val,0,sizeof val);
    memset(num,0,sizeof num);
    tot=0;
}
void Insert(ll d)
{
    int rt=0;
    for(int i=32; i>=0; --i)
    {
        int id=(d>>i)&1;
        if(!trie[rt][id]) trie[rt][id]=++tot;
        rt=trie[rt][id];
        num[rt]++;
    }
    val[rt]=d;
}
ll query(ll d)
{
    int rt=0;
    for(int i=32; i>=0; --i)
    {
        int id=(d>>i)&1;
        if(trie[rt][id^1]&&num[trie[rt][id^1]]) rt=trie[rt][id^1];
        else rt=trie[rt][id];
    }
    return d^val[rt];
}
void update(ll a,int b)
{
    int rt=0;
    for(int i=32; i>=0; --i)
    {
        int id=(a>>i)&1;
        rt=trie[rt][id];
        num[rt]+=b;
    }
}
int main()
{
    int q;
    char ch;
    init();
    scanf("%d",&q);
    getchar();
    while(q--)
    {
        ll x;
        scanf("%c%lld%*c",&ch,&x);
        if(ch=='+')
            Insert(x);
        else if(ch=='-')
            update(x,-1);
        else if(ch=='?')
            printf("%lld\n",max(query(x),x));
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值