CSU-ACM2017暑假集训比赛8 - A - Xor Sum - HDU - 4825

A - 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

对于这题,人类的智慧就是字典树。题中的“异或”让人很容易地想到利用数的二进制形式来解决这一题。具体方法也简单:将样例数字按位取反,与模式数字比较,和取反结果相比契合程度最高的模式数字就是所求结果(异或后得到的1最多)。例如模式数字 3  4  5 ,它们的32位二进制数位分别为

00000000 00000000 00000000 00000011(2)00000000 00000000 00000000 00000100(2)00000000 00000000 00000000 00000101(2)

对样例数字 1 来说,对它按位取反得到
11111111 11111111 11111111 11111110(2)

可以看出,~ 1 (1取反后)的二进制数与 4 的二进制数契合度最高,只有它们拥有两个共同的二进制位(低位第一位、低位第三位)。显然,契合程度最高的两个数异或后得到的结果是最大的。

为实现上述比较,只需要使用长度为32个元素的数组记录模式数字和样例数字的二进制形式;将模式数字的二进制数组当成字符串,建立字典树,叶节点保存该子树对应的十进制数,再将模式数组代入字典树,能同步下行则同步下行,不能同步下行则行至另一分支。重复这个过程,走到叶节点,提取模式数字的十进制形式即得答案。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int maxNode = 33e5, sigma_size = 2;
int ch[maxNode][sigma_size];
int val[maxNode];

struct Trie{
    int sz;
    Trie(){
        sz = 1;
        memset(ch[0], 0, sizeof(ch[0]));
    }
    int idx(char c){
        return c - 'a';
    }
    void insert(char *s, int v){
        int u = 0, n = 32;
        for(int i = 0; i < n; i++){
            int c = s[i];
            if(!ch[u][c]){
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] = v;
    }
    int query(char *s){
        int u = 0, n = 32;
        for(int i = 0; i < n; i++){
            int c = s[i];
            if(ch[u][c])
                u = ch[u][c];
            else
                u = ch[u][!c];
        }
        return val[u];
    }
};

int main(){
#ifdef TEST
freopen("test.txt", "r", stdin);
#endif

    int Case = 1, N, M;
    cin >> N;
    while(cin >> N >> M){
        Trie Td;
        unsigned int x;
        for(int i = 0; i < N; i++){
            scanf("%d", &x);
            int memoryX = x;
            char pattern[32];
            memset(pattern, 0, sizeof(pattern));
            int pos = 31;
            while(x){
                pattern[pos--] = x&1;
                x >>= 1;
            }
            Td.insert(pattern, memoryX);
        }
        printf("Case #%d:\n", Case++);
        for(int i = 0; i < M; i++){
            scanf("%d", &x);
            x = ~x;
            char text[32];
            int pos = 31;
            while(x){
                text[pos--] = x&1;
                x >>= 1;
            }
            printf("%d\n", Td.query(text));
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值