咕了好久终于来写了

题意

给你个点的无向完全图,每个点有点权,每两个点之间的边权是点权的值。问最小生成树边权和。

思路

从别人那里学来的巧妙思路,我们考虑树,首先,我们先把样例从高位到低位插入线性基。容易发现,对于每个叶子节点,既每个点值之间,是需要互相连边,那么求他们以后的边的异或值即可。由此可得,若是的深度越深,便越优。因为我们是从高位到低位插入的,所以浅的点权值较大,要尽量避免选择浅的点。我们把可能是的点找出来,可以发现如果均不相等那么这样的点就恰好有个。如果有,我们建一条边连接,权值为,对答案完全没有影响。
我们每找到这样的点,就暴力贪心下去:
每次尽量同时走左儿子或右儿子;
如果两个都有,就两个都走,然后取
如果两个只有不一样的儿子,就在返回值加上这一深度的值,然后继续走

代码

#include<bits/stdc++.h>
#define int long long
const int N=1e5+5,INF=0x3f3f3f3f,mod=998244353;
using namespace std;

int n;
int son[2][N*30+10],tot,ans;

inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}

int qpow(int a,int b)
{
    int ans=1;
    while(b){if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;}
    return ans;
}

void Insert(int a)
{
    int now=0,id;
    for(int i=30;i>=0;i--)
    {
        id=(a>>i)&1;
        if(!son[id][now])son[id][now]=++tot;
        now=son[id][now];
    }
}

int find(int r1,int r2,int b)
{
    if(b<0) return 0;
    int a1=-1,a2=-1;
    if(son[0][r1]&&son[0][r2]) a1=find(son[0][r1],son[0][r2],b-1);
    if(son[1][r1]&&son[1][r2]) a2=find(son[1][r1],son[1][r2],b-1);
    if(~a1&&~a2) return min(a1,a2);
    if(~a1) return a1;
    if(~a2) return a2;
    if(son[1][r1]&&son[0][r2]) a1=find(son[1][r1],son[0][r2],b-1)+(1<<b);
    if(son[0][r1]&&son[1][r2]) a2=find(son[0][r1],son[1][r2],b-1)+(1<<b);
    if(~a1&&~a2) return min(a1,a2);
    if(~a1) return a1;
    if(~a2) return a2;
}

void dfs(int a,int b)
{
    if(b<0) return;
    if(son[0][a]&&son[1][a]) ans+=1ll*find(son[0][a],son[1][a],b-1)+(1ll<<b);
    if(son[0][a]) dfs(son[0][a],b-1);
    if(son[1][a]) dfs(son[1][a],b-1);
}

signed main()
{
    n=read();
    for(int i=1;i<=n;i++)
        Insert(read());
    dfs(0,30);
    printf("%lld\n",ans);
    return 0;
}