UESTC 1582 奇迹的魔法啊,再度出现! 二进制树(字典树的一种特殊情况)

奇迹的魔法啊,再度出现!

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 131072/131072KB (Java/Others)


Submit  Status
想对你说的话,如山似海。——AsahinaMirai




决战之后,魔法界和无魔法界分离,为了和Rico再次相见,Mirai必须用经受考验,唤醒奇迹的魔法。
那考验就是——出现了nn个非负整数a1,a2,…,an,
对于m次询问,第jj次询问给定一个正整数xj,
输出max{a1XORxj,a2XORxj,…,anXORxj}。


XOR运算:0XOR1=1,1XOR0=1,0XOR0=0,1XOR1=0。
按位XOR运算:对两个数的二进制位依次进行XOR运算。
还对XOR运算不懂的请去百度或谷歌一下:异或。


Input

输入的第一行为一个正整数n,
接下来一行是n个非负整数a1,a2,…,an。
接下来为一个正整数m,
接下来一行,为m个非负整数x1,x2,...,xm。


Output

输出m行,每行1个值,表示所求答案。


Sample input and output

Sample Input Sample Output
5
1 8 0 5 14
5
7 14 10 4 2
15
15
15
12
12


Hint

1≤n≤100000,
1≤m≤100000,
0≤ai≤2147483647,
0≤xi≤2147483647


Source

17暑假前集训-数据结构专题 By AutSky_JadeK,思路非原创

2017 UESTC Training for Data Structures

UESTC 1582 奇迹的魔法啊,再度出现!


My Solution

题意:给出n个非负整数a1,a2,…,an
对于m次询问,第j次询问给定一个正整数xj,
输出max{a1XORxj,a2XORxj,…,anXORxj}。


二进制树(字典树的一种特殊情况)
先把所有的ai按照31比特位存入到二进制树(不够的相当于在前面补了0),
child[x][k] 表示以x为父节点, k 为边, 的子节点
sz[x]表示这个节点的值, 值为0的时候节点不存在


查询的时候,从根开始走,尽量去走和val的当前二进制为不相等的节点, 
此时的二进制异或为1,k = ((val >> i) & 1) ^ 1, res ^= 1 << i;
如果没有再走二进制为相等的节点且把加上的(1<<i)重新去掉 
if(!sz[child[x][k]]) k ^= 1, res ^= 1 << i;
每次直接走完31位(不存在的时候都是+0,没有影响), res就是答案了
复杂度 O(n*32)


#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 8;

int child[maxn*31][2], sz[maxn*31], tot = 1;
inline void modify(int val, int d)
{
    int k, x = 1;
    for(int i = 30; i >= 0; i--){
        k = (val >> i) & 1;
        if(!child[x][k]) child[x][k] = ++tot;
        sz[x] += d;
        x = child[x][k];
    }
    sz[x] += d;
}
inline int query(int val)
{
    int k, x = 1, res = 0;
    for(int i = 30; i >= 0; i--){
        k = ((val >> i) & 1) ^ 1, res ^= 1 << i;
        if(!sz[child[x][k]]) k ^= 1, res ^= 1 << i;
        x = child[x][k];
    }
    return res;
}


int main()
{
    #ifdef LOCAL
    freopen("f.txt", "r", stdin);
    //freopen("f.txt", "w", stdout);
    int T = 1;
    while(T--){
    #endif // LOCAL
    //ios::sync_with_stdio(false); cin.tie(0);

    int n, m, i, x;
    scanf("%d", &n);
    for(i = 0; i < n; i++){
        scanf("%d", &x);
        modify(x, 1);
    }
    scanf("%d", &m);
    for(i = 0; i < m; i++){
        scanf("%d", &x);
        printf("%d\n", query(x));
    }


    #ifdef LOCAL
    cout << endl;
    }
    #endif // LOCAL
    return 0;
}


  Thank you!

                                                                                                                                             ------from ProLights

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值