• 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:
    ", ca);
            while(m--){
                LL tmp;
                scanf("%lld", &tmp);
                printf("%lld
    ", 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
    ", 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
    ", Query(tmp)^tmp);
        }DelTrie(root);
        return 0;
    }
    View Code
  • 相关阅读:
    CSS禁止换行
    oracle时间转换:12小时24小时制
    三层架构各层次的职责
    Oracle截取字符串和查找字符串
    "......"的类型初始值设定项引发异常
    Oracle中对现有表增加列
    CSS 设置table 样式
    Aspose.Cells 根据Excel模板导出数据统计
    利用正则表达式限制网页表单里的文本框输入内容
    Table 边框 css设置
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/7611187.html
Copyright © 2020-2023  润新知