G. Xor-MST time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output
You are given a complete undirected graph with n vertices. A number ai is assigned to each vertex, and the weight of an edge between vertices i and j is equal to ai xor aj.
Calculate the weight of the minimum spanning tree in this graph.
Input
The first line contains n (1 ≤ n ≤ 200000) — the number of vertices in the graph.
The second line contains n integers a1, a2, …, an (0 ≤ ai < 230) — the numbers assigned to the vertices.
Output
Print one number — the weight of the minimum spanning tree in the graph.
Examples input Copy
5 1 2 3 4 5
output Copy
8
input Copy
4 1 2 3 4
output Copy
8
思路:问题关键在于如何划分顶点为不同的集合,使得每个集合中的顶点权重异或值是最小的。根据位操作的性质,可以从高位到低位依次取相似度最大的数字,每一位都把相同的数字归为一个集合,递归下去。
如二进制数字{1,10,11,100,101,110},首先划分为:{{1,10,11},{100,101,110}} ,后一个集合再次划分为{{100,101},110}等;合并两个集合时,把一个集合中的数字都添加到一个二叉树中,枚举另一个集合中的所有数字和这个二叉树中所有值的最小异或值。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,n) for(int i=(a);i<(n);++i)
#define per(i,a,n) for(int i=(n-1);i>=(a);--i)
#define pb push_back
#define mp make_pair
const int Inf=1e9;
#define N 200010
#define M 26
int a[N];
ll sum=0;
int rt[N*30][2];
int cnt;
void Insert(int x){
int k=0;
for(int i=29;i>=0;--i){
int id=1&(x>>i);
if(rt[k][id]==0){
rt[k][id]=cnt++;
k=rt[k][id];
rt[k][0]=rt[k][1]=0;
}
else{
k=rt[k][id];
}
}
}
int find_val(int x){ //查找与x异或值最小的元素
int val=0,k=0;
for(int i=29;i>=0;--i){
int id=1&(x>>i);
if(rt[k][id]==0) id^=1;
if(id){
val|=(1<<i);
}
k=rt[k][id];
}
return val; //当val==x时,x^val=0
}
void dfs(int id,int l,int r){
if(id<0) return ; //(二进制)从最高位开始,一直划分到个位
int mid=l;
for(int i=l;i<r;++i){
if((a[i]>>id)&1){
mid=i;
break;
}
}
if(mid<r) dfs(id-1,mid,r); // (1<<id)==1的划分为一个集合
if(l<mid) dfs(id-1,l,mid); // (1<<id)==0的划分为一个集合
if(l<mid&&mid<r){ //id==0时,只有个位不同,异或值为1
cnt=0;
rt[cnt][0]=rt[cnt][1]=0; ++cnt; //同一数据结构重复使用,记得初始化为0
for(int i=l;i<mid;++i) Insert(a[i]);
int val=(1<<30);
for(int i=mid;i<r;++i) val=min(val,a[i]^find_val(a[i]));
sum+=val;
}
}
int main(){
ios::sync_with_stdio(0);
int n;
cin>>n;
for(int i=0;i<n;++i){
cin>>a[i];
}
sort(a,a+n); //排序为下一步根据大小划分集合
dfs(29,0,n);
cout<<sum;
return 0;
}