• HDU 4825 Xor Sum (裸字典树+二进制异或)


    题目链接

    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

    分析:
    首先我们将数字转换为33位的二进制,不足33位的用0补充,然后根据每个数字的二进制位建立字典树。(需要注意的一点就是我们把一个数字转换为二进制后肯定是倒序的,但是我们建树的时候需要按照正序的来建树,所以建树的时候是要将求出的二进制位倒序建树)。

    接下来对于每个查询,同样处理成33位二进制字符串,因为要求的是异或值最大,我们可以将每个位置的0存成1,1存成0,不足33位的全部用1填充,所以我们在查询的时候,如果当前位置已经建立过字典树,也就相当于与原来的值不相同,接着按照这个位置往下走(这样走下去的值是最大的),如果当前位置没有建立过字典树(也就是说与该二进制维相反的原来没有数字,那就只能走与它相同的了),也就相当于当前位置为0的话就走1,为1的话就走0。

    代码:

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    using namespace std;
    int num[55];
    int val[6000000];
    int tree[6000000][2];
    int flag;
    void init()
    {
        memset(tree,0,sizeof(tree));
        memset(val,0,sizeof(val));
        flag=1;
    }
    void build(int a)
    {
       int u=0;
       for(int i=33;i>=1;i--)
       {
           if(tree[u][num[i]]==0)//没有值,就构建值
           {
               tree[u][num[i]]=flag++;
           }
            u=tree[u][num[i]];
       }
       val[u]=a;
    }
    
    int query()
    {
        int u=0;
        for(int i=33;i>=1;i--)
        {
            if(tree[u][num[i]]==0)//相当于与该位置的值相反的字典树中没有,那就只能按照相同的往下走了
                u=tree[u][num[i]^1];
            else
                u=tree[u][num[i]];//字典树中由值,在高位就异或后为1了,接着走异或值会更大
        }
        return val[u];
    }
    int main()
    {
        int T,n,m,a,cnt;
        scanf("%d",&T);
        for(int t=1;t<=T;t++)
        {
            init();
            printf("Case #%d:
    ",t);
            scanf("%d%d",&n,&m);
            for(int i=0;i<n;i++)
            {
                memset(num,0,sizeof(num));
                scanf("%d",&a);
                cnt=0;
                int b=a;
                while(b)//这里还是按照原本的二进制位置存储
                {
                    cnt++;
                    if(b&1) num[cnt]=1;
                    else num[cnt]=0;
                    b>>=1;
                }
                build(a);
            }
    
            for(int i=0;i<m;i++)
            {
                scanf("%d",&a);
                cnt=0;
                while(a)//注意这里,是将每一个位置的二进制树都存反了(1存成0,0存成1)
                {
                    cnt++;
                    if(a&1) num[cnt]=0;
                    else num[cnt]=1;
                    a>>=1;
                }
                for(int j=cnt+1;j<=33;j++)
                    num[j]=1;
                printf("%d
    ",query());
            }
        }
        return 0;
    }
    
  • 相关阅读:
    .NET CORE 部署3
    Filezilla
    Java 项目转换为maven项目教程
    Andriod studio 汉化教程
    tarjan好题
    关于二分的边界
    2019-10-11
    诗人小G(1D1D动态规划)
    斜率优化dp(玩具装箱)
    扩展欧几里得定律
  • 原文地址:https://www.cnblogs.com/cmmdc/p/7875345.html
Copyright © 2020-2023  润新知