Xor Sum
题意:
第一行給定一个整数 T ,表示有 T 组测试数据;
每组数据第一行給定两个整数 n、m,表示集合中的元素个数和询问次数;
接下来一行有 n 个整数,这 n 个整数构成一个集合a;
之后 m 行,每行一个整数 q 代表询问的正整数。
对于每次询问,找出集合中和 q 异或结果最大的一个数。
数据范围:
T<10,1<=N,M<=100000,所有正整数均不超过2^32
解题思路:
01字典树
01字典树经常用于解决异或问题,这道题就是一个经典例子,刚开始做这道题的时候一脸蒙圈,不晓得怎么是用字典树来做,后来终于理清了思路,其实不难,就是把集合中的正整数以二进制的形式存到字典树中去
,然后每次询问时,把询问的正整数也表示成二进制形式,再从最高位
开始搜索字典树,寻找集合中和q异或结果最大的那个数。
但有一点要注意,那就是数组的大小
,最糟糕的情况是,有100000个数(即n=100000),且每个数都有独立的32个节点(32位的二进制数),那么占用内存大小就是100000*32=3.2e6,当然这是不可能的,因为整数的二进制形式只有0和1,所以肯定有些数要和其它数共用某些节点,亲测,2e6+5
是能过的,但1e6+5就太小了,不过这题给的内存空间比较大,所以即使就开3.2e6
大的数组也哦可。
2e6+5:
Exe.Time : 358MS | Exe.Memory : 23480K
3.2e6:
Exe.Time : 405MS | Exe.Memory : 32872K
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
using namespace std;
#define INF 0x3f3f3f
#define zero 1e-7
typedef long long ll;
const int N=3.2e6+5;
int trie[N][2], quary[N];
int k;
void init() {
k=0;
memset(trie, 0, sizeof(trie));
return ;
}
void inSert(int u[], int v) {
int p=0;
for(int i=0; i<32; i++) {
int c=u[i];
if(!trie[p][c])
trie[p][c]=++k;
p=trie[p][c];
quary[p]=v;
}
return ;
}
int Search(int num[], int q) {
int p=0;
for(int i=0; i<32; i++) {
int c=num[i];
if(trie[p][c^1]) {
p=trie[p][c^1];
}
else p=trie[p][c];
}
return quary[p];
}
int main() {
int T, n, m, a, q;
scanf("%d", &T);
for(int t=1; t<=T; t++) {
init();
scanf("%d %d", &n, &m);
for(int i=0; i<n; i++) {
scanf("%d", &a);
int b[32]={0}, d=a;
for(int j=31; j>=0; j--) {
b[j]=d&1;//b是a的二进制形式
d>>=1;
}
inSert(b, a);
}
printf("Case #%d:\n", t);
for(int i=0; i<m; i++) {
scanf("%d", &q);
int b[32]={0}, d=q;
for(int j=31; j>=0; j--) {
b[j]=d&1;//b是q的二进制形式
d>>=1;
}
printf("%d\n", Search(b, q));
}
}
return 0;
}