Description
描述
给一个长度为 $n$ 的数组 $a$,试找到一个 $X$,使得 $maxlimits_{1le i le n} (a_i oplus X)$ 最小,输出这个最小值即可。
输入
第一行一个数 $n(1 le n le 10^5)$,接下来第二行 $n$ 个数表示 $a$ 数组 $(0le a_i le 2^{30} - 1)$。
输出
一行一个数,表示所求最小值。
样例
输入1
3
1 2 3输出1
2
输入2
2
1 5输出2
4
解释
在第一个样例中,我们可以选 $X = 3$。
在第一个样例中,我们可以选 $X = 5$。
Solution
看到异或,很自然想到从高位到低位贪心。如果较高的位置是能设为 $0$ 的,那一定将它设为 $0$,这样的答案一定最优。
我们可以对数集建 01-trie,为了适应从高位到低位的贪心,我们将第一层(也就是根节点所在的那一层)的 $dep$ 设为 $30$,从上到下依次 $-1$,得到一个总深度为 $30$ 的 trie,$dep = i$ 的层表示的是 $i$ 位的数字,位数从 $0$ 开始计算,最高到 $29$ 位,可以满足题目中 $a_i < 2^{30}$ 的要求。
比如样例,有三个数 $1, 2, 3$,我们的 01-trie 是这个样子的:
$ullet$ 插入 $1$:
$ullet$ 插入 $2$:
$ullet$ 插入 $3$:
插入的代码不难写出:
void insert(LL rt, LL val, LL dep) { o[rt].dep = dep; if(!dep) return; if(!o[rt].ch[val >> dep - 1 & 1]) o[rt].ch[val >> dep - 1 & 1] = ++siz; insert(o[rt].ch[val >> dep - 1 & 1], val, dep - 1); }
然后就可以在 01-trie 上 DP,在每个结点维护一个 $res$,表示以它为根的树的答案。比如现在要求 $res_{rt}$ 根据树形 DP 的基本操作,当然是先递归它的儿子,然后分类讨论一下:
$ullet$ 如果 $rt$ 只有一个儿子,那么如果是 $ch_0$,$X$ 的这位就设成 $0$(保持不变),否则就设为 $1$(取反),总有办法使得这个位置的答案是 $0$,所以直接返回该儿子的 $res$ 即可
$ullet$ 如果 $rt$ 两个儿子都有,那么如果 $X$ 这位设成 $0$,$ch_1$ 的该位就依然是 $1$;如果设为 $1$,$ch_0$ 的该位就变成了 $1$,既然 $2^{dep_{rt} - 1}$($-1$ 因为是儿子那位的答案,写成 $2 ^{dep_{son}}$ 也行)逃不掉了,在此基础上加上 $min(res_{ch_0}, res_{ch_1})$ 再返回即可
DP 的代码:
void solve(LL rt) { if(o[rt].ch[0]) solve(o[rt].ch[0]); if(o[rt].ch[1]) solve(o[rt].ch[1]); if(o[rt].ch[0] && o[rt].ch[1]) o[rt].res = min(o[o[rt].ch[0]].res, o[o[rt].ch[1]].res) + (1LL << o[rt].dep - 1); else o[rt].res = o[o[rt].ch[0]].res + o[o[rt].ch[1]].res; }
最后根节点 $1$ 的 $res$ 就是答案,完整的代码给出,供参考。
#include <bits/stdc++.h> #define ROOT 1 using namespace std; typedef long long LL; const int N = 2e5 + 5; struct node { LL res, dep, ch[2]; node() { res = dep = ch[0] = ch[1] = 0; } } o[N * 20]; LL a[N], n, siz = 1; void insert(LL rt, LL val, LL dep) { o[rt].dep = dep; if(!dep) return; if(!o[rt].ch[val >> dep - 1 & 1]) o[rt].ch[val >> dep - 1 & 1] = ++siz; insert(o[rt].ch[val >> dep - 1 & 1], val, dep - 1); } void solve(LL rt) { if(o[rt].ch[0]) solve(o[rt].ch[0]); if(o[rt].ch[1]) solve(o[rt].ch[1]); if(o[rt].ch[0] && o[rt].ch[1]) o[rt].res = min(o[o[rt].ch[0]].res, o[o[rt].ch[1]].res) + (1LL << o[rt].dep - 1); else o[rt].res = o[o[rt].ch[0]].res + o[o[rt].ch[1]].res; } int main() { ios::sync_with_stdio(false); cin >> n; for(int i = 1; i <= n; i++) { cin >> a[i]; insert(ROOT, a[i], 30); } solve(ROOT); cout << o[ROOT].res << endl; return 0; }