01字典树及其模板

模板 :

const int Bit = 32;
struct Trie
{
    Trie *ch[2];
    LL v;
    inline void init(){
        for(int i=0; i<2; i++) this->ch[i] = NULL;
        this->v = -1;
    }
};

Trie *root;///记得在 main 函数的时候分配空间给 root 并初始化

inline void CreateTrie(LL x)
{
    Trie * p = root, *tmp;
    for(int i=Bit; i>=0; i--){
        int idx = (x>>i)&1;
        if(p->ch[idx] == NULL){
            tmp = (Trie *)malloc(sizeof(Trie));
            tmp->init();
            p->ch[idx] = tmp;
        }//else  //加个权值啥的
        p = p->ch[idx];
    }
    p->v = x;///在结尾打上标记,表示这串二进制的十进制表示是多少
}

LL Query(LL x)
{
    Trie *p = root;
    for(int i=Bit; i>=0; i--){
        int idx = (x>>i)&1;
        if(p->ch[idx^1]){
            p = p->ch[idx^1];
        }else p = p->ch[idx];
    }
    return p->v;
}

inline void DelTrie(Trie *T)
{
    if(T == NULL) return ;
    for(int i=0; i<2; i++){
        if(T->ch[i]){
            DelTrie(T->ch[i]);
        }
    }free(T);
}
指针版
const int maxn = 100000 + 5;        //集合中的数字个数
typedef long long LL;
int ch[32 * maxn][2];               //节点的边信息
LL value[32 * maxn];                //节点存储的值
int node_cnt;                       //树中当前节点个数

inline void init(){                 //树清空
    node_cnt = 1;
    memset(ch[0],0,sizeof(ch));
}
inline void Insert(LL x){           //在字典树中插入 X
                                    //和一般字典树的操作相同 将X的二进制插入到字典树中
    int cur = 0;
    for(int i = 32;i >= 0;--i){
        int idx = (x >> i) & 1;
        if(!ch[cur][idx]){
            memset(ch[node_cnt],0,sizeof(ch[node_cnt]));
            ch[cur][idx] = node_cnt;
            value[node_cnt++] = 0;
        }
        cur = ch[cur][idx];
    }
    value[cur] = x;                 //最后的节点插入value
}
LL Query(LL x){              //在字典树中查找和X异或的最大值的元素Y 返回Y的值
    int cur = 0;
    for(int i = 32;i >= 0;--i){
        int idx = (x >> i) & 1;
        if(ch[cur][idx ^ 1]) cur = ch[cur][idx ^ 1];
        else cur = ch[cur][idx];
    }
    return value[cur];
}
数组版

 

01字典树是求区间连续的异或和以及异或最大值的有利工具

参考博客 ==> 

http://blog.csdn.net/guhaiteng/article/details/52191831  

http://blog.csdn.net/solardomo/article/details/52168853 

http://blog.csdn.net/miracle_ma/article/details/51484054

 

相关题目 :

 HDU 4825

题意 : 给出一个数集,然后给出 M 个询问,每个问询是一个数 S ,问你数集中哪个数和 S 异或后的值是最大的

分析 : 模板题

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100000 + 5;        //集合中的数字个数
typedef long long LL;
int ch[32 * maxn][2];               //节点的边信息
LL value[32 * maxn];                //节点存储的值
int node_cnt;                       //树中当前节点个数

inline void init(){                 //树清空
    node_cnt = 1;
    memset(ch[0],0,sizeof(ch));
}
inline void Insert(LL x){           //在字典树中插入 X
                                    //和一般字典树的操作相同 将X的二进制插入到字典树中
    int cur = 0;
    for(int i = 32;i >= 0;--i){
        int idx = (x >> i) & 1;
        if(!ch[cur][idx]){
            memset(ch[node_cnt],0,sizeof(ch[node_cnt]));
            ch[cur][idx] = node_cnt;
            value[node_cnt++] = 0;
        }
        cur = ch[cur][idx];
    }
    value[cur] = x;                 //最后的节点插入value
}
LL Query(LL x){              //在字典树中查找和X异或的最大值的元素Y 返回Y的值
    int cur = 0;
    for(int i = 32;i >= 0;--i){
        int idx = (x >> i) & 1;
        if(ch[cur][idx ^ 1]) cur = ch[cur][idx ^ 1];
        else cur = ch[cur][idx];
    }
    return value[cur];
}
int main(void)
{
    int nCase, n, m;
    scanf("%d", &nCase);
    for(int ca=1; ca<=nCase; ca++){
        init();
        scanf("%d %d", &n, &m);
        for(int i=0; i<n; i++){
            LL tmp;
            scanf("%lld", &tmp);
            Insert(tmp);
        }
        printf("Case #%d:\n", ca);
        while(m--){
            LL tmp;
            scanf("%lld", &tmp);
            printf("%lld\n", Query(tmp));
        }
    }
    return 0;
}
View Code

 

HDU 5536

题意 : 在一个数集里面找出三不同的数,使得 ( i + j )^k 最大,并输出这个最大的结果

分析 : 为了达到 i、j、k 三个数都不同的效果,我们需要对01字典树添加上删除操作,只要在结构体里面加上一个标记标识就行了,具体看代码实现,很简单

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int Bit = 32;
struct Trie
{
    Trie *ch[2];
    LL v;
    int pass;
    inline void init(){
        for(int i=0; i<2; i++)
            this->ch[i] = NULL;
        this->v = 0;
        this->pass = 1;///标记
    }
};

Trie *root;

inline void RunTrie(LL x, bool isDel)
{
    Trie * p = root, *tmp;
    for(int i=Bit; i>=0; i--){
        int idx = (x>>i)&1;
        if(!isDel){
            if(p->ch[idx] == NULL){
                tmp = (Trie *)malloc(sizeof(Trie));
                tmp->init();
                p->ch[idx] = tmp;
            }else p->ch[idx]->pass += 1;
            p = p->ch[idx];
        }else{
            p->ch[idx]->pass -= 1;
            p = p->ch[idx];
        }
    }
    if(!isDel) p->v = x;
}

LL Query(LL x)
{
    Trie *p = root;
    for(int i=Bit; i>=0; i--){
        int idx = (x>>i)&1;
        if(p->ch[idx^1] && p->ch[idx^1]->pass > 0){
            p = p->ch[idx^1];
        }else p = p->ch[idx];
    }
    return (p->v)^x;
}

inline void DelTrie(Trie *T)
{
    if(T == NULL) return ;
    for(int i=0; i<2; i++){
        if(T->ch[i]){
            DelTrie(T->ch[i]);
        }
    }free(T);
}

LL arr[1001];

int main(void)
{
    int nCase, n, m;
    scanf("%d", &nCase);
    while(nCase--){
        root = (Trie *)malloc(sizeof(Trie));
        root->init();

        scanf("%d", &n);
        for(int i=0; i<n; i++){
            scanf("%lld", &arr[i]);
            RunTrie(arr[i], false);
        }
        LL ans = 0;
        for(int i=0; i<n; i++){
            RunTrie(arr[i], true);///删除 i 先
            for(int j=0; j<n; j++){
                if(i != j){
                    RunTrie(arr[j], true);///删除 j
                    ans = max(ans, Query(arr[i]+arr[j]));///查询
                    RunTrie(arr[j], false);///再把 j 加回来
                }
            }
            RunTrie(arr[i], false);///把 i 加回来
        }
        printf("%I64d\n", ans);
        DelTrie(root);
    }
    return 0;
}
View Code

 

Codeforces 706 D

题意 : 在一个本来就包含 0 的数集里面进行三种操作 ' + ' 添加一个数、 ' - ' 删去一个数、' ? ' 数集里面哪两个数的异或值最大,输出

分析 : 跟上一题如出一辙,不过要小心本来就包含 0 这个陷阱

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int Bit = 30;
struct Trie
{
    Trie *ch[2];
    LL v;
    int pass;
    inline void init(){
        for(int i=0; i<2; i++) this->ch[i] = NULL;
        this->v = -1;
        this->pass = 1;
    }
};

Trie *root;

inline void TrieOP(LL x, bool del)
{
    Trie * p = root, *tmp;
    for(int i=Bit; i>=0; i--){
        int idx = (x>>i)&1;
        if(!del){
            if(p->ch[idx] == NULL){
                tmp = (Trie *)malloc(sizeof(Trie));
                tmp->init();
                p->ch[idx] = tmp;
            }else p->ch[idx]->pass++;
            p = p->ch[idx];
        }else{
            p = p->ch[idx];
            p->pass -= 1;
        }
    }
    if(!del) p->v = x;
}

LL Query(LL x)
{
    Trie *p = root;
    for(int i=Bit; i>=0; i--){
        int idx = (x>>i)&1;
        if(p->ch[idx^1] && p->ch[idx^1]->pass > 0){
            p = p->ch[idx^1];
        }else p = p->ch[idx];
    }
    return p->v;
}

inline void DelTrie(Trie *T)
{
    if(T == NULL) return ;
    for(int i=0; i<2; i++){
        if(T->ch[i]){
            DelTrie(T->ch[i]);
        }
    }free(T);
}

int main(void)
{
    int nOperator;
    root = (Trie *)malloc(sizeof(Trie));
    root->init();
    TrieOP((long long)0, false);
    scanf("%d", &nOperator);
    while(nOperator--){
        char ch;
        LL tmp;
        scanf(" %c", &ch);
        scanf("%I64d", &tmp);
        if(ch == '+') TrieOP(tmp, false);
        else if(ch == '-') TrieOP(tmp, true);
        else if(ch == '?') printf("%I64d\n", Query(tmp)^tmp);
    }DelTrie(root);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/LiHior/p/7611187.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值