☆ [HDU4825] Xor Sum「最大异或和(Trie树)」

传送门:>Here<

题意:给出一个集合,包含N个数,每次询问给出一个数x,问x与集合中的一个数y异或得到最大值时,y是多少?

解题思路

  由于N,M非常大,暴力显然不行。抓住重点是异或,所以可以把数字转换为二进制。这又让我们想到了字典树……

  根据二进制中数的定理:任何一个位置靠前的数比后面所有的数加起来都大。就好像十进制,100比99大。因此异或的时候要尽量让靠前的数与它不一样。

  把集合内的所有数字根据二进制建立字典树。很明显因为是二进制只有0和1,当前的trie一定是一棵二叉树。因此我们可以拿当前数到trie里面跑一遍,因为trie是前缀树,所以先碰到的肯定是二进制前的。如果有不同的路(就是说如果当前数字为0,看有没有通往1的路)就走,否则没办法

  注意到数字最多只有32位。因为要建立前缀树,所以如果数字太小可以补充前导零。每一次用一个bool数组记录一下数字的各个位即可

Code

  End数组要清零

/*By DennyQi*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 3300010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); return x * w;
}
int T,N,M,x;
int ch[MAXN][2],cnt,End[MAXN];
bool num[70];
inline void Convert(int x){
    memset(num, 0, sizeof(num));
    for(int i = 33; x > 0; --i, x>>=1) num[i] = x%2;
}
inline void Insert(int x){
    Convert(x);
    int u = 0;
    for(int i = 1; i <= 33; ++i){
        if(!ch[u][num[i]]) ch[u][num[i]] = ++cnt;
        u = ch[u][num[i]];
    }
    End[u] = x;
}
inline int Query(int x){
    Convert(x);
    int u = 0, res = 0;
    for(int i = 1; i <= 33; ++i){
        if(!ch[u][!num[i]]) u = ch[u][num[i]];
        else u = ch[u][!num[i]];
        if(End[u]) res = Max(res, End[u]);
    }
    return res;
}
inline void Init(){
    cnt = 0;
    memset(ch, 0, sizeof(ch));
    memset(End, 0, sizeof(End));
}
int main(){
    T=r;
    for(int _c = 1; _c <= T; ++_c){
        printf("Case #%d:
",_c);
        Init();
        N=r,M=r;
        for(int i = 1; i <= N; ++i) x=r, Insert(x);
        for(int i = 1; i <= M; ++i) x=r, printf("%d
", Query(x));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/qixingzhi/p/9425017.html