题意:一个n个点的完全图,点带权,边权是两端点点权的异或值。问你最小生成树。
一个性质,把所有点按照二进制最高位是否为1划分为2个集合,那么这两个集合间只会有一条边。可以递归处理。
把所有点建成01Trie,发现两个集合就是Trie的每个结点的两个子树。用启发式的思想,在小子树里dfs到叶子结点,取出每个值,然后去大子树里查询即可。
O(n(logn)^2)。
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int ch[100005*32][2],sz,siz[100005*32];
void Insert(int x)
{
int U=0;
for(int i=31-1;i>=0;--i){
if(!ch[U][(x>>i)&1]){
ch[U][(x>>i)&1]=++sz;
}
U=ch[U][(x>>i)&1];
}
}
int query(int x,int U,int nidep){
int res=0;
for(int i=nidep;i>=0;--i){
bool Bit=((x>>i)&1);
if(!ch[U][Bit]){
res|=(1<<i);
Bit^=1;
}
U=ch[U][Bit];
}
return res;
}
ll ans;
void dfsz(int U){
if(!ch[U][0] && !ch[U][1]){
siz[U]=1;
}
if(ch[U][0]){
dfsz(ch[U][0]);
siz[U]+=siz[ch[U][0]];
}
if(ch[U][1]){
dfsz(ch[U][1]);
siz[U]+=siz[ch[U][1]];
}
}
int nowans;
void df2(int U,int now,int rtnidep,int nidep,int otherrt){
if(!ch[U][0] && !ch[U][1]){
nowans=min(nowans,query(now,otherrt,rtnidep-1));
}
if(ch[U][0]){
df2(ch[U][0],now,rtnidep,nidep-1,otherrt);
}
if(ch[U][1]){
df2(ch[U][1],now|(1<<(nidep-1)),rtnidep,nidep-1,otherrt);
}
}
void dfs(int U,int nidep){
if(ch[U][0] && ch[U][1]){
nowans=2147483647;
if(siz[ch[U][0]]>siz[ch[U][1]]){
df2(ch[U][1],0,nidep,nidep,ch[U][0]);
}
else{
df2(ch[U][0],0,nidep,nidep,ch[U][1]);
}
nowans|=(1<<nidep);
ans+=nowans;
}
if(ch[U][0]){
dfs(ch[U][0],nidep-1);
}
if(ch[U][1]){
dfs(ch[U][1],nidep-1);
}
}
int n,m;
int main(){
// freopen("b.in","r",stdin);
int x;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&x);
Insert(x);
}
dfsz(0);
dfs(0,30);
printf("%lld\n",ans);
return 0;
}