题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4825
题意:给你n个数字,再给你m次询问,每次询问给你一个数S,求n个数中和S异或最大的值。
题解:n方绝对超时。所以需要用的01字典树这个东西。
01字典树:本质和字典树是一样的。只不过他每个节点只存0和1这两个东西。具体来说一下。我们可以把一个数字转成2进制数,把这个数字用来建树。但是这个树是从高位置向低位置建树的。比如3不是11,而是0000……11。
对于建树来说。若当前这个位置为1那么就在tire【root】【1】这个位置加一个节点。若是0那就trie【root】【0】这个位置加一个节点。然后root = tire【root】【1/0】;
对于查询来说。每次判断tire【root】【i^1】是否有值。有就让root = trie【root】【i^1】,没有就root = tire【root】【i】;
具体看代码注释:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
typedef long long ll;
using namespace std;
const int maxn = 1e5+7;
int ch[32*maxn][2];
ll val[32*maxn];
int sz;
void init(){
memset(ch[0],0,sizeof(ch[0]));
sz = 1;
}
void insert(ll a){ // 插入a
int u = 0; // 从根节点0插入
for(int i = 32 ; i >= 0 ; i --){ // 从高位开始遍历。
int c = ((a >> i) & 1); // 判断 i 位置 是 0 还是 1
if(!ch[u][c]){ // 若当前节点没有值
memset(ch[sz],0,sizeof(ch[sz])); // 建立一个新的节点。
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c]; // 遍历到u的第c个儿子节点
}
val[u] = a; // 叶子节点赋值
}
ll query(ll a){
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]; // 向不同的地方走
else u = ch[u][c];
}
return val[u]; //返回叶子节点存的值。
}
ll a[maxn];
int main(){
int t,n,m,kase = 0;
scanf("%d",&t);
while(t--){
init();
scanf("%d%d",&n,&m);
for(int i = 1 ; i <= n ; i ++){
scanf("%lld",&a[i]);
insert(a[i]); // 插入值
}
printf("Case #%d:\n",++kase);
for(int i = 1 ; i <= m ; i ++){
ll x;
scanf("%lld",&x);
printf("%lld\n",query(x)); // 查询值
}
}
return 0;
}