题意:
给你一张完全图,每个点都有一个值,每条边的值是连接两个点的异或,问你这张图里面的最小生成树的权值和是多少。
题解:
异或最小我们用tire树来找的话一定是使得这两个数尽量在同一个子树上,那么我们就可以这样做:直接将所有的数放入一个字典树里面,那么相同的数相连一定是0,所以相同的数只保留一个即可,接下来一个子树一个子树做,两个不相交的集合一定是左子树上找一个值与右子树上找一个值异或最小,那么我们只需要枚举较小的子树的值,在较大的子树上找最小异或即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
int tr[N*32][2],p[N*32],cnt=1,a[N];
vector<int>vec[N];
void add(int s,int id)
{
int now=1;
for(int i=30;i>=0;i--)
{
int &ne=tr[now][((s>>i)&1)];
if(!ne)
ne=++cnt;
now=ne;
}
p[now]=id,vec[id].push_back(a[id]);
}
int finds(int root,int s,int dep)
{
int now=root;
int ans=0;
for(int i=dep-1;i>=0;i--)
{
int ne=tr[now][((s>>i)&1)];
if(!ne)
ans|=(1<<i),ne=tr[now][(1^((s>>i)&1))];
now=ne;
}
return ans|(1<<dep);
}
ll ans;
void dfs(int x,int dep)
{
if(!x||!dep)
return ;
int ls=tr[x][0],rs=tr[x][1];
dfs(ls,dep-1),dfs(rs,dep-1);
if(!ls||!rs)
{
p[x]=p[ls+rs];
return ;
}
int lid=p[ls],rid=p[rs];
if(vec[rid].size()>vec[lid].size())
swap(ls,rs),swap(lid,rid);
int mi=2e9;
for(int i=0;i<vec[rid].size();i++)
{
mi=min(mi,finds(ls,vec[rid][i],dep-1));
vec[lid].push_back(vec[rid][i]);
}
vec[rid].clear();
p[x]=p[ls];
ans+=(ll)mi;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
if(a[i]!=a[i-1])
add(a[i],i);
dfs(1,31);
printf("%lld\n",ans);
return 0;
}