【HDU 4825】Xor Sum——杨子曰题目
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(1)是时间去异或一下求个max来的快
别急,有没有发现我们要比较的是询问的这个数和集合中所有数的前缀,说道前缀,你应该要想到一个东东叫做——trie字典树(那是什么鬼!戳),相信有人已经恍然大悟了,于是乎我们得到了以下思路:
1.把所有集合中的数以01串的形式从高位往地位放进trie里面(注意高位不足补齐32位)
2.对于询问的数换成二进制以后从高位开始与线段树上的点匹配,如果这一位是0就往1走,如果这一位是1就往0走,如果要走的点不存在,那我们只好顺着存在的点走(每个点(除叶子节点)都至少有一个儿子,因为所有串的长度都是32)
3.输出走到叶子后找到的那个数
OK,完事
c++代码
#include<bits/stdc++.h>
using namespace std;
int sum=1,l;
int tr[2000000][2];
long long flag[2000000],maxx=0;
void build(long long s){
int k=1;
for (int i=31;i>=0;i--){
int p=s&(1<<i)?1:0;
if (!tr[k][p]) {
tr[k][p]=++sum;
}
k=tr[k][p];
}
flag[k]=s;
}
long long find(long long s){
int k=1;
for (int i=31;i>=0;i--){
int p=s&(1<<i)?1:0;
k=tr[k][p^1]?tr[k][p^1]:tr[k][p];
}
return flag[k];
}
int main(){
int cas;
scanf("%d",&cas);
for (int i=1;i<=cas;i++){
printf("Case #%d:\n",i);
sum=1;
memset(tr,0,sizeof(tr));
int n,m;
scanf("%d%d",&n,&m);
while(n--){
long long k;
maxx=max(k,maxx);
scanf("%lld",&k);
build(k);
}
while(m--){
long long k;
scanf("%lld",&k);
printf("%lld\n",find(k));
}
}
return 0;
}
与XJ机房607