1.将每个点的权值转换成二进制,从高位往低位依次插入01字典树.
2.dfs遍历该字典树的每个结点,如果某个结点有两个子节点,这两个子节点的子树分别会构成两个连通块,要在这两个连通块之间各选一个点连边并使它们的异或值最小,通过find函数递归处理这个问题.ans还要加上此时产生的二进制位对应的值.
3.find函数中先考虑能不能同时往0子节点或同时往1子节点走,此时这两条边异或值为0,返回值取这两种走法的最小值.如果不能同时往0或往1,只能一个往1一个往0,此时这两条边异或值不为零,返回值要加上此时产生的二进制位对应的值,还要加上这两种走法继续向下的最小值.
时间复杂度O(30*n+(n-1)*30)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll inf=1e18;
const int N=200010;
int a[N];
struct node
{
int s[2],r[2];
}trie[30*N+10];
int con=1;
int Pow[36];
void insert(int x)
{
int w[36],id=1;
while(x)
{
if(x&1)w[id++]=1;
else w[id++]=0;
x>>=1;
}
for(int i=id;i<=30;++i)
w[id++]=0;
int now=0;
for(int i=30;i>=1;--i)
{
if(!trie[now].s[w[i]])
{
trie[now].s[w[i]]=con;
trie[now].r[w[i]]++;
now=con++;
}
else
{
now=trie[now].s[w[i]];
trie[now].r[w[i]]++;
}
}
}
ll ans=0;
ll find(int x,int y,int d)
{
if(!trie[x].s[0]&&!trie[x].s[1])
return 0;
ll res=inf,f=0;
if(trie[x].s[0]&&trie[y].s[0])
{
f=1;
res=min(res,find(trie[x].s[0],trie[y].s[0],d-1));
}
if(trie[x].s[1]&&trie[y].s[1])
{
f=1;
res=min(res,find(trie[x].s[1],trie[y].s[1],d-1));
}
if(!f)
{
if(trie[x].s[0]&&trie[y].s[1])
res=min(res,find(trie[x].s[0],trie[y].s[1],d-1));
if(trie[x].s[1]&&trie[y].s[0])
res=min(res,find(trie[x].s[1],trie[y].s[0],d-1));
res+=Pow[d];
}
return res;
}
void dfs(int now,int d)
{
if(trie[now].s[0]&&trie[now].s[1])
ans+=Pow[d]+find(trie[now].s[0],trie[now].s[1],d-1);
if(trie[now].s[0])
dfs(trie[now].s[0],d-1);
if(trie[now].s[1])
dfs(trie[now].s[1],d-1);
}
int main()
{
// freopen("1.txt","r",stdin);
int n;cin>>n;
Pow[1]=1;
for(int i=2;i<=30;++i)
Pow[i]=Pow[i-1]*2;
for(int i=0;i<n;++i)
{
int x;scanf("%d",&x);
insert(x);
}
dfs(0,30);
cout<<ans<<endl;
return 0;
}