以前一直以为字典树没有多少用,但是最近一直碰到(难道是以前刷题太少的原因么),其中有一类问题叫做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