题目链接:D. Perfect Security
题目大意:给定一个数组,要从另外一个数组中找到依次找到一个数,是它和数组对应的值的异或值最小。
题目思路:标准的01字典树,其中涉及字典树的构建,添加,查找和删除,可以作为一个典型的字典树模板题。
个人理解,如果字典树是26叉树,那么01字典树就是2叉树。
先上代码,在代码里面解释:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define bit(x) x&(-x)
using namespace std;
const int maxn =3e5+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
ll n;
ll a[maxn];
ll s[maxn*32][2]; //s数组是建立的树;
ll v[maxn*32],sum[maxn*32][2]; //v数组用来标记在该点是否有插入的值,sum标记树的每一个点在建树的时候被遍历了几次。
ll to=0;
void unit()
{
memset(s,0,sizeof(s)); //对数组进行清零
memset(v,0,sizeof(v));
memset(sum,0,sizeof(sum));
}
void add(ll w) //int最多位移31位,所以开为long long;
{
int root=0;
for(int i=32;i>=0;i--){
int r=(w>>i)&1;
if(!s[root][r]){
s[root][r]=++to;
}
sum[root][r]++; //在建树过程中把经历过的点都加一
root=s[root][r]; //root相当于一个指针
}
v[root]=w; //每录入完一个数,把他最后一个root的v数组的值加如x记录下来;
}
ll find(ll a)
{
int root=0; //这里在查找的时候就进行了删除
for(int i=32;i>=0;i--){
int id=(a>>i)&1;
if(sum[root][id]){ //只有在建树的时候被遍历过的点才可以选;贪心的选取最小的
sum[root][id]--;
root=s[root][((a>>i)&1)];
}
else{
sum[root][id^1]--; //表示这个值已经被找到了,把该值删去。
root=s[root][((a>>i)&1)^1];
}
}
//cout<<v[root]<<endl;
return v[root];
}
int main()
{
while(~scanf("%lld",&n)){
unit();
to=0;
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(ll i=1;i<=n;i++){
ll k;
scanf("%lld",&k);
add(k);
}
for(ll i=1;i<=n;i++){
printf("%lld\n",a[i]^find(a[i]));
}
}
return 0;
}
下面是把查找和删除分开处理的代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define bit(x) x&(-x)
using namespace std;
const int maxn =3e5+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
ll n;
ll a[maxn];
ll s[maxn*32][2];
ll v[maxn*32],sum[maxn*32][2];
ll to=0;
void unit()
{
memset(s,0,sizeof(s));
memset(v,0,sizeof(v));
memset(sum,0,sizeof(sum));
}
void add(ll w)
{
int root=0;
for(int i=32;i>=0;i--){
int r=(w>>i)&1;
if(!s[root][r]){
s[root][r]=++to;
}
sum[root][r]++;
root=s[root][r];
}
v[root]=w;
}
ll find(ll a)
{
int root=0;
for(int i=32;i>=0;i--){
int id=(a>>i)&1;
if(sum[root][id]){
root=s[root][((a>>i)&1)];
}
else{
root=s[root][((a>>i)&1)^1];
}
}
//cout<<v[root]<<endl;
return v[root];
}
void del(ll a)
{
int root=0;
for(int i=32;i>=0;i--){
int id=(a>>i)&1;
sum[root][id]--;
root=s[root][id];
}
//v[root]=0;
}
int main()
{
while(~scanf("%lld",&n)){
unit();
to=0;
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(ll i=1;i<=n;i++){
ll k;
scanf("%lld",&k);
add(k);
}
for(ll i=1;i<=n;i++){
printf("%lld\n",a[i]^find(a[i]));
del(find(a[i]));
}
}
return 0;
}