异或值
给定一个长度为 $n$ 的整数序列 $a_1,a_2, \ldots, a_n$。
请你找到一个非负整数 $X$,使得 $\max\limits_{1 \leq i \leq n} \{ a_i \oplus X \}$ 的值尽可能小,其中 $\oplus$ 表示按位异或。
输出 $\max\limits_{1 \leq i \leq n} \{ a_i \oplus X \}$ 的最小可能值。
输入格式
第一行包含整数 $n$。
第二行包含 $n$ 个整数 $a_1,a_2,\ldots,a_n$。
输出格式
一个整数,表示 $\max\limits_{1 \leq i \leq n} \{ a_i \oplus X \}$ 的最小可能值。
数据范围
前 $3$ 个测试点满足 $1 \leq n \leq 3$。
所有测试点满足 $1 \leq n \leq {10}^{5}$,$0 \leq a_i \leq {2}^{30}−1$。
输入样例1:
3 1 2 3
输出样例1:
2
输入样例2:
2 1 5
输出样例2:
4
解题思路
先把每个数的二进制表示用Trie存下来,然后在Trie上dfs求异或结果的最小值。
从二进制的最高位(第$29$位)开始看,$X$的最高位有两种选择,即$0$和$1$。由于异或运算对每个位是独立的,我们可以先递归求出两棵子树(即$0$和$1$两个方向)在第$28$位到第$0$位中异或结果的最大值的最小值,假设存在且为$a$(对应$0$那个分支的子树)和$b$(对应$1$那个分支的子树)。如果$X$的最高位是$0$,那么异或结果的最大值的最小值就是$\max \{ a, b + 2^{29} \}$。同理如果$X$的最高位是$1$,那么异或结果的最大值的最小值就是$\max \{ a + 2^{29}, b \}$。最终的答案就是这两种情况的最小值,即$\min \left\{ \max \{ a, b + 2^{29} \}, \ \max \{ a + 2^{29}, b \} \right\}$。
更一般的,如果递归到$X$的第$d$位,那么第$d$位到第$0$位的异或结果的最大值的最小值就是$$\min \left\{ \max \{ a, b + 2^{d} \}, \ \max \{ a + 2^{d}, b \} \right\}$$
其中如果$0$对应的那个分支不存在,那么令$a = -\infty$。如果$1$对应的那个分支不存在,那么令$b = -\infty$。
然后是时间复杂度的问题,看上去好像每层递归都有两个分支,时间复杂度为$O(2^{30})$。实则不然,因为Trie中最多只有$30 \times n$个节点,因此时间复杂度应该是$O(30 \times n)$。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 30e5 + 10; 5 6 int tr[N][2], idx; 7 8 void insert(int x) { 9 int p = 0; 10 for (int i = 29; i >= 0; i--) { 11 int t = x >> i & 1; 12 if (!tr[p][t]) tr[p][t] = ++idx; 13 p = tr[p][t]; 14 } 15 } 16 17 int dfs(int u, int d) { 18 if (d == -1) return 0; 19 int a = -2e9, b = -2e9; 20 if (tr[u][0]) a = dfs(tr[u][0], d - 1); 21 if (tr[u][1]) b = dfs(tr[u][1], d - 1); 22 return min(max(a, b + (1 << d)), max(a + (1 << d), b)); 23 } 24 25 int main() { 26 int n; 27 scanf("%d", &n); 28 while (n--) { 29 int x; 30 scanf("%d", &x); 31 insert(x); 32 } 33 printf("%d", dfs(0, 29)); 34 35 return 0; 36 }
参考资料
AcWing 4869. 异或值(AcWing杯 - 周赛):https://www.acwing.com/video/4644/
标签:int,max,tr,29,leq,异或 From: https://www.cnblogs.com/onlyblues/p/17181584.html