You are given a complete undirected graph with n n n vertices. A number a i a_{i} ai? is assigned to each vertex, and the weight of an edge between vertices i i i and j j j is equal to a i x o r a j a_{i}xora_{j} ai?xoraj? .
Calculate the weight of the minimum spanning tree in this graph.
The first line contains n n n ( 1 < = n < = 200000 1<=n<=200000 1<=n<=200000 ) — the number of vertices in the graph.
The second line contains n n n integers a 1 a_{1} a1? , a 2 a_{2} a2? , …, a n a_{n} an? ( 0 < = a i < 2 30 0<=a_{i}<2^{30} 0<=ai?<230 ) — the numbers assigned to the vertices.
Print one number — the weight of the minimum spanning tree in the graph.
5
1 2 3 4 5
8
4
1 2 3 4
8
以上来自洛谷 以上来自洛谷 以上来自洛谷
先讲一种算法:当前每个集合伸出去一条最短的边,然后把联通块缩成一个新的集合,用这种思想处理
M
S
T
MST
MST。(学习
M
S
T
MST
MST 看这里)
但是如果我们要使得异或和最小,这个很容易想到
01
T
r
i
e
01Trie
01Trie(学习
T
r
i
e
Trie
Trie 树看这里),考虑将每一个点按照权值插到
T
r
i
e
Trie
Trie 中,这样我们考虑叶子节点就是实际存在的点,而非叶子节点一定时若干对叶子节点的
L
C
A
LCA
LCA。(不知道
L
C
A
LCA
LCA?看这里)
根据二进制的性质,不难发现我们先合并
L
C
A
LCA
LCA 深度大的点对更优,这样我们考虑
dfs
?
\operatorname{dfs}
dfs 整个
T
r
i
e
Trie
Trie,对于一个非叶子节点,我们枚举他的
0
/
1
0/1
0/1 子树的所有儿子,然后在
1
/
0
1/0
1/0 子树中找到边权最小的来合并,如果我们从小到大插入,那么编号就是连续的,然后启发式合并即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int Maxn = 6e6 + 5;
int n, a[Maxn];
int rt, L[Maxn], R[Maxn], ch[Maxn][2], tot;
inline void Insert(int &now, int x, int dep) {
if (!now) {
now = ++tot;
}
L[now] = min(L[now], x), R[now] = max(R[now], x);
if (dep < 0) {
return;
}
int bit = a[x] >> dep & 1;
Insert(ch[now][bit], x, dep - 1);
}
inline int query(int now, int val, int dep) {
if (dep < 0) {
return 0;
}
int bit = val >> dep & 1;
if (ch[now][bit]) {
return query(ch[now][bit], val, dep - 1);
} else {
return query(ch[now][bit ^ 1], val, dep - 1) + (1 << dep);
}
}
inline int dfs(int now, int dep) {
if (dep < 0) {
return 0;
}
if (R[ch[now][0]] && R[ch[now][1]]) {
int minn = INT_MAX;
for (int i = L[ch[now][0]]; i <= R[ch[now][0]]; i++) {
minn = min(minn, query(ch[now][1], a[i], dep - 1));
}
return dfs(ch[now][0], dep - 1) + dfs(ch[now][1], dep - 1) + minn + (1 << dep);
}
if (R[ch[now][0]]) {
return dfs(ch[now][0], dep - 1);
}
if (R[ch[now][1]]) {
return dfs(ch[now][1], dep - 1);
}
return 0;
}
inline void work() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + n + 1);
memset(L, 0x3f, sizeof(L));
for (int i = 1; i <= n; i++) {
Insert(rt, i, 30);
}
cout << dfs(rt, 30) << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
work();
return 0;
}