![](https://i-blog.csdnimg.cn/blog_migrate/b6e37fb3991b76be71759b343e373ba8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f7b144b2f0288ad23606899b21b49cbc.png)
建出trie之后
考虑从根节点往下贪心考虑(高位到低位)
考虑节点要是分叉的情况我们序列中必然有数该位上为1
这样我们需要考虑往左往右走才是更优的
要是我们往左走了 就相当于可以不考虑右边的子树了 因为他们那边连高位的‘1’都没有显然不可能是最大值
要是不分叉我们就直接走就是了
然后考虑dp[u]:表示节点u子树包括的数异或后(ans)的最小值是什么
转移自然就只有三种 分叉一种 不分叉两种
分叉的话显然是左右两边的最小值+现在该位必为‘1’ dp[u]=min(dp[l],dp[r])+1<<(该位)
不分叉直接dp[u]=dp[]即可
题目让我们找到一个X ,使得X异或ai的最大值最小,并求出这个最小可能值。
所以我们其实不用求X,我们在trie上dp,直接求得最终的这个最小可能值。
如果在trie上一个节点往下有分叉,那么我们序列中必然有数该位上为1和0,那么任何X异或后,最终值的该位上必为1,所以要使最终值最小,此时最终值应为 min(left,right) + (1<<该位);
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 3e6 + 100;
int tr[N][2], idx;
int n;
int dp[N];
void insert(int x) {
int p = 0;
for(int i = 30; i >= 0; i--) {
int u = x >> i & 1;
if(!tr[p][u]) tr[p][u] = ++idx;
p = tr[p][u];
}
}
void dfs(int u, int dep) {
int l = tr[u][0], r = tr[u][1];
if(l) dfs(l, dep - 1);
if(r) dfs(r, dep - 1);
if(l && r) dp[u] = min(dp[l], dp[r]) + (1 << dep);
else if(l) dp[u] += dp[l];
else if(r) dp[u] += dp[r];
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
int x;
cin >> x;
insert(x);
}
dfs(0, 30);
cout << dp[0] << endl;
return 0;
}