Boruvka算法+01trie树 现在在#3上re,可能访问到了负数节点
#include<bits/stdc++.h>
using namespace std;
/*
对全部点建一个trie树
对于一个联通块,建立一个trie树,只包含当前联通块里的节点
每次查询一个联通块里的一个点x到其他联通块里的最小异或值,
就是在整个trie树中选择一个节点,使得与a[x]异或值最小(按位计算)
注:使用联通块trie树当前子树的大小和
大trie树当前子树的大小作比较 去除选择当前联通块里的点
合并两个联通块时,就要合并两个trie树
*/
const int N=2e5+5;
struct node{//trie树节点
int ch[2],siz,point;//ch:孩子编号,-1为没有孩子 siz:子树大小 point:该权值对应的节点编号
node(int si,int pi){
siz=si,point=pi;
ch[0]=ch[1]=-1;
}
};
int n,a[N];
int f[N],minn[N],other[N];//minn:每个点的最小边长 other:对应的另一个节点
int root[N];//root:每个联通块trie树的根,0表示全部点的trie树
vector<node>trie;//所有trie树的所有节点
void insert(int x,int pnt,int w){//x:当前根,pnt:该权值对应节点
int t;
trie[x].siz++;
for(int i=30;i>=0;i--){
t=(w>>i)&1;//该位w的子树选取
if(trie[x].ch[t]==-1){
trie.push_back(node(1,-1));
x=trie[x].ch[t]=trie.size()-1;
}
else{
x=trie[x].ch[t];
trie[x].siz++;
}
}
trie[x].point=pnt;//存入该权值对应节点
}
inline int getsizec(int x,int id){
if(x<0)return 0;
if(trie[x].ch[id]<0)return 0;
return trie[trie[x].ch[id]].siz;
}
inline int getpoint(int x){
if(x<0)return -1;
return trie[x].point;
}
pair<int,int> query(int r1,int r2,int w){//r1:大trie树,r2:要去除的trie树,w:和另一个值异或
int t,ans=0;
for(int i=30;i>=0;i--){
t=(w>>i)&1;//该位w
if(trie[r1].ch[t]!=-1&&trie[trie[r1].ch[t]].siz-getsizec(r2,t)>0){//若可以与t相同,则异或为0,就选它
// 表示有这个子树 表示:在大trie树的当前子树中有除了当前联通块的节点
r1=trie[r1].ch[t];//跳到子树
r2=trie[r2].ch[t];
// cout<<i<<'\n';
}
else{
// cout<<i<<'t'<<endl;
r1=trie[r1].ch[t^1];//跳到另一个子树
r2=trie[r2].ch[t^1];
// if(r1<0)cout<<"x1"<<r1<<endl;
// if(r2<0)cout<<"x2"<<r2<<endl;
ans=ans|(1<<i);//加入最终异或值
}
}
return make_pair(ans,getpoint(r1));//能达到的最小异或值,即其对应节点
}
void merge(int &r1,int r2){//合并两棵trie
if(r1==-1){
r1=r2;
return ;
}
if(r2==-1){
r1=r1;
return ;
}
merge(trie[r1].ch[0],trie[r2].ch[0]);
merge(trie[r1].ch[1],trie[r2].ch[1]);
trie[r1].siz=getsizec(r1,0)+getsizec(r1,1);
// trie[r1].point=trie[r2].point;
return ;
}
int findf(int x){
if(f[x]==x)return x;
f[x]=findf(f[x]);
return f[x];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
n=unique(a+1,a+n+1)-a-1;
for(int i=0;i<=n;i++){//建总根
trie.push_back(node(0,-1));
root[i]=trie.size()-1;
}
for(int i=1;i<=n;i++){
insert(root[0],i,a[i]);
insert(root[i],i,a[i]);
}
for(int i=1;i<=n;i++)f[i]=i;
int fu,fv,ischanged,cnt=0,ans=0;
while(true){
memset(minn,0x3f,sizeof(minn));
ischanged=false;
for(int i=1;i<=n;i++){
fu=findf(i);
pair<int,int> t=query(root[0],root[fu],a[i]);//查找最小边
if(t.second==-1)continue;
fv=findf(t.second);
if(fu==fv)continue;
ischanged=true;
if(minn[fu]>t.first)minn[fu]=t.first,other[fu]=fv;
if(minn[fv]>t.first)minn[fv]=t.first,other[fv]=fu;
}
if(!ischanged)break;
ischanged=false;
for(int i=1;i<=n;i++){
fu=findf(i),fv=findf(other[i]);
if(minn[i]<0x3f3f3f3f&&fu!=fv){
ischanged=true;
cnt++;
ans+=minn[i];
merge(root[fu],root[fv]);
f[fv]=fu;
}
}
if(!ischanged)break;
if(cnt==n-1)break;
}
printf("%d",ans);
return 0;
}