首先:01字典树是一种特殊的字典树形式,它的所有节点(除了每条链最底层的子结点)所包含的值都是0或1,也就是要把一个数字拆成二进制的形式才能存入链中。而在字典树的末端都会有一个节点存储了一个数的十进制形式,也就是从上到下各个01的组合可以组成的数字。这个数据结构可以用来求最大异或和所对应的元素或者最大异或和本身。
为什么可以这么说呢?因为当查询当前数字和树内某个数可以产生最大异或值时,查询总是向本数字当前位的反面进行的——也就是说,当前位是1,就去找在这一层上有没有0,当前位是0,就去找这一层上有没有1,一个贪心思路。因为总是试图找到与当前位相异的值,而在每条链的底部都对应一个数字,所以这样可以确保找到与当前数产生最大异或值所对应的数。
本题是01字典树的模板题。
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define reset(a, b) memset(a, b, sizeof(a))
const int INF = 0x3f3f3f3f;
using namespace std;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1000000007;
const int tool_const = 19991126;
const int tool_const2 = 33;
inline ll lldcin()
{
ll tmp = 0, si = 1;
char c;
c = getchar();
while (c > '9' || c < '0')
{
if (c == '-')
si = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
tmp = tmp * 10 + c - '0';
c = getchar();
}
return si * tmp;
}
///Untersee Boot IXD2(1942)
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
/**Last Remote**/
ll rootvalue[5000000],trees[5000000][3];
ll cnt=0;
inline void ins(ll x)
{
ll rt=0;
for(int i=32;i>=0;i--)
{
int nxt=(x>>i)&1;
if(trees[rt][nxt]==0)
{
trees[rt][nxt]=++cnt;
}
rt=trees[rt][nxt];
}
rootvalue[rt]=x;
}
inline ll query(ll x)
{
ll rt=0;
for(int i=32;i>=0;i--)
{
int nxt=(x>>i)&1;
if(trees[rt][nxt^1])//反向查找
rt=trees[rt][nxt^1];
else
rt=trees[rt][nxt];
}
return rootvalue[rt];
}
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
ll t,ct=1;
cin>>t;
while(t--)
{
ll n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
trees[i][0]=trees[i][1]=0;
for(int i=1;i<=n;i++)
{
ll tmp;
cin>>tmp;
ins(tmp);
}
cout<<"Case #"<<ct++<<":"<<endl;
for(int i=1;i<=m;i++)
{
ll tmp2;
cin>>tmp2;
cout<<query(tmp2)<<endl;
}
}
return 0;
}