01trie树
定义:将整数转化为二进制再插入 trie 树,通常是从高位到低位插入trie。
使用场景:寻找与异或相关的结果
题目背景:
Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeus 发起M次询问,每次询问中包含一个正整数 S ,之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大。Prometheus 为了让 Zeus 看到人类的伟大,随即同意 Zeus 可以向人类求助。你能证明人类的智慧么?
输入:
输入包含若干组测试数据,每组测试数据包含若干行。
输入的第一行是一个整数T(T < 10),表示共有T组数据。
每组数据的第一行输入两个正整数N,M(<1=N,M<=100000),接下来一行,包含N个正整数,代表 Zeus 的获得的集合,之后M行,每行一个正整数S,代表 Prometheus 询问的正整数。所有正整数均不超过2^32。
输出:
对于每组数据,首先需要输出单独一行”Case #?:”,其中问号处应填入当前的数据组数,组数从1开始计算。
对于每个询问,输出一个正整数K,使得K与S异或值最大。
样例:
输入:
2
3 2
3 4 5
1
5
4 1
4 6 5 6
3
输出:
Case #1:
4
3
Case #2:
4
题目大意:
对于每一个 s 在 n 个数中找一个,使得与其异或值最大。
步骤:
1.将n个整数,转化成二进制,从高位到低位插入 trie 树
2.对于询问的整数 s ,从高位开始与trie树中的结点比对:若 s[i] = 0 ,则尝试走 trie 中值为 1 的结点,反之尝试走 trie 中值为 0 的结点。(这样异或值最大)
这样说可能会有些抽象。
举一个例子: n 个数中第一个是 29 ,就先把它化成 2 进制,也就是 11101 ,注意前面要加零凑够 32 位数于是我们得到了一棵树(此处省略前面的零, 1 为右节点, 0 为左节点):
这时第二个数是 24 ,我们发现它的一部分与 29 相同,就只需要补齐其他的节点了,这个图里,两个数都可以找到:
像这样我们把 n 个数都映射到 trie 树上下一步肯定有对应的数。这题正好是异或操作,我们只需要把每一个 s 从高位到低位寻找树上的点(高位为 1 肯定更大),因为异或值当前位为 1 ,两个异或的数的这一位肯定不同,所以对于 s 的每一位,如果为 1 去 0 (左节点)否则反之,当然没有期望的数就只能走相反的一边了。
Code:
#include<bits/stdc++.h> using namespace std; int tree[3300000][2]; int tot,n,q,t,now; int xx[100001]; void insert(int x){ int u=0; for(int i=31;i>=0;i--){ int k=(x>>i)&1; if(!tree[u][k]){ tree[u][k]=++tot; } u=tree[u][k]; } return; } int search(int x){ long long ans=0; int u=0; for(int i=31;i>=0;i--){ int k=(x>>i)&1; if(tree[u][k^1]){ ans+=(k^1)<<i; u=tree[u][k^1]; } else{ ans+=k<<i; u=tree[u][k]; } } return ans; } void ans(){ for(int i=1;i<=n;i++){ int x; cin>>x; insert(x); } for(int i=1;i<=q;i++){ cin>>xx[i]; } cout<<"Case #"<<now<<":"<<endl; for(int i=1;i<=q;i++){ cout<<search(xx[i])<<endl; } } int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>t; now=0; while(t--){ now++; memset(tree,0,sizeof(tree)); tot=0; cin>>n>>q; ans(); } return 0; }
注意要点(坑):
1. 2的32次方 已经超出了 int 的范围;
2. 这题时间卡得很紧,读入不能直接 cin 要么加 ios 要么写 scanf。