分析

我们先手摸一下01Trie树的结构
发现我们需要做的就是将树上的某个点连通
由于各个位之间是独立的
我们肯定要贪心取位
发现,若当前点位
那么我们需要将他的左儿子和右儿子联通
一定是选择其中异或最小的一对进行连边
这时我们有一个比较暴力而自然的想法:
枚举左子树(右子树)包含的值
每个都在右边的Trie树上跑一边
但仔细打量一下,这个时间复杂度到底是多少呢
我们每次取
那么这个集合大小至少为原大小的一半
所以时间复杂度

发现这个非常能冲!
所以我们直接每次找两个子树内的最下异或打入答案即可
时间复杂度:
期望得分:100分

代码

@哲哥我比你短!!!

#include <bits/stdc++.h>
using namespace std;
const long long MaxN=2e5+10,MaxM=31;
long long Total,u,v,w,Opt,Ans,Num[MaxN],Base[MaxN],Ch[MaxN*20][2],Size=1;
vector<long long>Temp[MaxN*20];
inline long long Get(long long Loc,long long New,long long Dep) {
    if(Dep<0) { return 0; }
    long long x=((New>>Dep) & 1);
    if(Ch[Loc][x]) { return Get(Ch[Loc][x],New,Dep-1); }
    else { return Base[Dep]+Get(Ch[Loc][x ^ 1],New,Dep-1); }
}
inline void Solve(long long Loc,long long Dep) {
    long long a=Ch[Loc][0],b=Ch[Loc][1];
    if(a && b) {
        long long Min=0x3f3f3f3f3f3f3f3f;
        if(Temp[a].size()>Temp[b].size()) { swap(a,b); }
        for(int i=0;i<Temp[a].size();i++) Min=min(Min,Get(b,Temp[a][i],Dep-1));
        Ans+=Min+(1<<Dep);
    }
    if(a) { Solve(a,Dep-1); } if(b) { Solve(b,Dep-1); }
}
inline void Update(long long New) {
    long long Loc=1;
    for(int i=30;i>=0;i--) {
        long long x=((New>>(i)) & 1);
        if(!Ch[Loc][x]) Ch[Loc][x]=++Size;
        Loc=Ch[Loc][x];
        Temp[Loc].push_back(New);
    }
}
signed main() {
    scanf("%lld",&Total);
    Base[0]=1; for(int i=1;i<=MaxM-1;i++) { Base[i]=(Base[i-1]<<1); }
    for(int i=1;i<=Total;i++) { scanf("%lld",&u); Update(u); }
    Solve(1,30);
    printf("%lld\n",Ans);
}