01字典树

以前一直以为字典树没有多少用,但是最近一直碰到(难道是以前刷题太少的原因么),其中有一类问题叫做01字典树问题,它是用来解决xor的有力武器,通常是给你一个数组,问你一段连续的异或和最大是多少,正常思路贪心dp啥的都会一头雾水,但是用01字典树就能很快的解决,实现起来也十分方便。

将要插入的数的二进制位倒着建树(为什么?因为异或时高位尽量大,结果才尽量大),即高位在深度低的节点上

01字典树的普遍模版

#define Memset(x, a) memset(x, a, sizeof(x))
typedef long long ll;
const int maxn = 100000 + 5;//集合中的数字个数
int ch[32*maxn][2];         //节点的边信息
ll val[32*maxn];            //节点存储的值
int sz;                     //树中当前节点个数

void init(){
    Memset(ch[0],0);           //树清空
    sz=1;
}

void _insert(ll a){//在字典树中插入 a
                  //和一般字典树的操作相同 将X的二进制插入到字典树中
    int u=0;
    for(int i=32;i>=0;i--){
        int c=((a>>i)&1);
        if(!ch[u][c]){
            Memset(ch[sz],0);
            val[sz]=0;
            ch[u][c]=sz++;
        }
        u=ch[u][c];
    }
    val[u]=a;     //最后的节点插入value
}

ll query(ll a){   //在字典树中查找和a异或的值最大的元素b 返回b的值
    int u=0;
    for(int i=32;i>=0;i--){
        int c=((a>>i)&1);
        if(ch[u][c^1]) u=ch[u][c^1];//c=0,b=c^1=1,b^c=1;c=1,b=c^1=0,b^c=1;
        else u=ch[u][c];
    }
    return val[u];
}

中间的细节可以自己修改,比如有时可能会删除某个数,就需要记录这个节点走了多少次,如果次数为0,就不往下走,数组大小应该开32(64,如果是LL)*数组元素个数。


原链接

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

练习链接

http://acm.hdu.edu.cn/showproblem.php?pid=4825

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

猜你喜欢

转载自blog.csdn.net/ac_blood/article/details/80072784