解题过程转自:http://blog.csdn.net/xiaofengcanyuexj/article/details/26083887
Xor Sum
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 132768/132768 K (Java/Others)
Total Submission(s): 4445 Accepted Submission(s): 652
Problem Description
Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeus 发起M次询问,每次询问中包含一个正整数 S ,之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大。Prometheus 为了让 Zeus 看到人类的伟大,随即同意 Zeus 可以向人类求助。你能证明人类的智慧么?
Input
输入包含若干组测试数据,每组测试数据包含若干行。输入的第一行是一个整数T(T < 10),表示共有T组数据。每组数据的第一行输入两个正整数N,M(<1=N,M<=100000),接下来一行,包含N个正整数,代表 Zeus 的获得的集合,之后M行,每行一个正整数S,代表 Prometheus 询问的正整数。所有正整数均不超过2^32。
Output
对于每组数据,首先需要输出单独一行”Case #?:”,其中问号处应填入当前的数据组数,组数从1开始计算。对于每个询问,输出一个正整数K,使得K与S异或值最大。
Sample Input
2
3 2
3 4 5
1
5
4 1
4 6 5 6
3
Sample Output
Case #1:
4
3
Case #2:
4
题解
本题数据量很大,感觉直接暴力时间复杂度O(n*m)会超时,不过想到去年网赛有道题数据量也很大直接暴力竟然过了于是直接敲了敲,最终还是TLE了。想到位操作运算符的通用思想,逐位处理,对应本题还是比较容易想到字典树的,Memory Limit: 132768/132768 K (Java/Others),提示可以尝试牺牲空间换取时间,直接字典树空间消耗为2^32,但本题数据相对此规模还是比较小的,一共n个数,总共32*n位,所以空间复杂度为O(n),可以接受。先用原序列中的数按位建立字典树,然后将查找过程中用待询问数与0xffffffff异或XOR来在字典树上跑,最终找到的即为最大的。假设按查询的XOR的某个分支不存在,则想另一分支进行,这样答案可能变小,但是正确的,总的时间复杂度O(m*log(32)),常数可以不计。
转自:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAXN=100000*32+100;
__int64 tree[MAXN][2];
__int64 node[MAXN];
__int64 tot;
void insert(__int64 a)
{
__int64 j;
int bit,dep=0;
for(j=31;j>=0;j--)
{
bit=((0x1<<j)&a)?0x1:0x0;
if(tree[dep][bit]==0)
{
tree[dep][bit]=tot++;
memset(tree[tree[dep][bit]], 0, sizeof(tree[tree[dep][bit]]));
}
dep=tree[dep][bit];
}
node[dep]=a;
}
__int64 Solve(__int64 val)
{
__int64 j,bit;
int dep=0;
for(j=31;j>=0;j--)
{
bit=((0x1<<j)&val)?0x0:0x1;
if(tree[dep][bit])dep=tree[dep][bit];
else dep=tree[dep][bit^1];
}
return node[dep];
}
int main()
{
int cas;
__int64 n,m,j,s,a;
int tag=0;
cin>>cas;
while(cas--)
{
tot=1;
scanf("%I64d%I64d",&n,&m);
memset(tree[0],0,sizeof(tree[0]));
while(n--)
{
scanf("%I64d",&a);
insert(a);
}
printf("Case #%d:\n",++tag);
while(m--)
{
scanf("%I64d",&s);
printf("%I64d\n",Solve(s));
}
}
return 0;
}
字典树的与实现
Trie,又称字典树、单词查找树,是一种树形结构,用于保存大量的字符串。它的优点是:利用字符串的公共前缀来节约存储空间。
相对来说,Trie树是一种比较简单的数据结构.理解起来比较简单,正所谓简单的东西也得付出代价.故Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.
其基本性质可以归纳为:
1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3. 每个节点的所有子节点包含的字符都不相同。
其基本操作有:查找 插入和删除,当然删除操作比较少见.我在这里只是实现了对整个树的删除操作,至于单个word的删除操作也很简单.
搜索字典项目的方法为:
(1) 从根结点开始一次搜索;
(2) 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;
其他操作类似处理.
Name: Trie树的基本实现
Author: MaiK
Description: Trie树的基本实现 ,包括查找 插入和删除操作*/
#include<algorithm>
#include<iostream>
using namespace std;
const int sonnum=26, base='a';
struct Trie
{
int num;//to remember how many word can reach here,that is to say,prefix
bool terminal;//If terminal==true ,the current point has no following point
struct Trie *son[sonnum];//the following point
};
Trie *NewTrie() // create a new node
{
Trie *temp=new Trie;
temp->num=1;temp->terminal=false;
for(int i=0;i<sonnum;++i)temp->son[i]=NULL;
return temp;
}
void Insert(Trie *pnt, char *s, int len) // insert a new word to Trie tree
{
Trie *temp=pnt;
for(int i=0;i<len;++i)
{
if(temp->son[s[i]-base]==NULL)temp->son[s[i]-base]=NewTrie();
else temp->son[s[i]-base]->num++;
temp=temp->son[s[i]-base];
}
temp->terminal=true;
}
void Delete(Trie *pnt) // delete the whole tree
{
if(pnt!=NULL)
{
for(int i=0;i<sonnum;++i)if(pnt->son[i]!=NULL)Delete(pnt->son[i]);
delete pnt;
pnt=NULL;
}
}
Trie* Find(Trie *pnt, char *s, int len) // trie to find the current word
{
Trie *temp=pnt;
for(int i=0;i<len;++i)
if(temp->son[s[i]-base]!=NULL)temp=temp->son[s[i]-base];
else return NULL;
return temp;
}