以前一直以为字典树没有多少用,但是最近一直碰到(难道是以前刷题太少的原因么),其中有一类问题叫做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)*数组元素个数。
废话不多说,来看题把。
传送门:hdu 4825 Xor Sum
题意:O(-1)
思路:01字典树入门题,每个数都插入字典树中,然后查询即可AC。
#include
#define Memset(x, a) memset(x, a, sizeof(x))
using namespace std;
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];
}
int main(){
int T,n,m;
scanf("%d",&T);
for(int cas=1; cas<=T; cas++){
init(); //初始化
scanf("%d%d",&n,&m);
ll a;
for(int i=0; i
传送门:CF 706 D. Vasiliy's Multiset
题意:O(-1)
思路:一道01字典树的题。将要插入的数的二进制位倒着建树(为什么?因为异或时高位尽量大,结果才尽量大),即高位在深度低的节点上。用一个数组记录经过各个节点的数的个数,插入时,每经过一个点,将节点的这个值加一,删除时,则减一。查找时,当前节点的这个值大于0,说明有数经过。对于要查找的这个数的高位,如果是1,要使异或值尽量大,那么就要往0的地方走,反之,往1的地方走,实在没办法走,只有按原路径走啦。详见代码。注意:0永远在树中。
#include
using namespace std;
#define Memset(x, a) memset(x, a, sizeof(x))
typedef long long ll;
const int maxn = 222222;//集合中的数字个数
int ch[32*maxn][2]; //节点的边信息
ll val[32*maxn]; //节点存储的值
int sz,q; //树中当前节点个数
ll num;
void init(){
Memset(ch[0],0); //树清空
sz=1;
}
void _insert(ll a){
int u=0;
for(int i=32;i>=0;i--){
int c=((a>>i)&1);
if(!ch[u][c]){
Memset(ch[sz],0);
ch[u][c]=sz++;
}
u=ch[u][c];
++val[u];
}
}
void _delete(ll a){
int u=0;
for (int i=32; i>=0; --i){
int c=((a>>i)&1);
u=ch[u][c];
--val[u];
}
}
ll query(ll a){
int u=0;
ll ans=0;
for(int i=32;i>=0;i--){
int c=((a>>i)&1);
if (c==1){
if (ch[u][0] && val[ch[u][0]]){
ans+=1<>q;
_insert(0);
while(q--){
cin>>op>>num;
if(op[0]=='+')_insert(num);
else if(op[0]=='-')_delete(num);
else cout<
num--;
,恢复的时候
num++;
即可,十分简单。