#最小生成树,Trie#CF888G Xor-MST
题目
给定 \(n\) 个结点的无向完全图。每个点有一个点权为 \(a_i\) 。
连接 \(i\) 号结点和 \(j\) 号结点的边的边权为 \(a_i\oplus a_j\) 。
求这个图的 MST 的权值。\(n\leq 2*10^5,0\leq a_i<2^{30}\)
分析
考虑将所有点分为两个集合,一个最高位为0,一个最高位为1
两个集合内部相互连边一定是最优的,那么就是要在这两个集合之间找一条边权最小的边
选取大小更小的集合直接匹配最小异或值,时间复杂度\(O(nlog^2n)\)
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
typedef long long lll;
const int N=6000011;
int n,a[200011];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed min(int a,int b){return a<b?a:b;}
struct _01Trie{
int trie[N][2],L[N],R[N],fail[N],tot;
inline void BUILD(){trie[0][0]=trie[0][1]=tot=1;}
inline void Insert(int x,int rk){
rr int p=1;
for (rr int i=29;~i;--i){
rr int z=(x>>i)&1;
if (!trie[p][z]) trie[p][z]=++tot;
p=trie[p][z],R[p]=rk;
if (!L[p]) L[p]=rk;
}
}
inline signed query(int p,int x,int i){
if (i<0) return 0;
rr int z=(x>>i)&1;
if (trie[p][z]) return query(trie[p][z],x,i-1);
else return query(trie[p][z^1],x,i-1)|(1<<i);
}
inline lll dfs(int p,int i){
if (i<0) return 0;
rr int ls=trie[p][0],rs=trie[p][1];
if (!rs) return dfs(ls,i-1);
if (!ls) return dfs(rs,i-1);
if (R[ls]+L[rs]>R[rs]+L[ls]) ls^=rs,rs^=ls,ls^=rs;
rr int ans=1<<30;
for (rr int j=L[ls];j<=R[ls];++j)
ans=min(ans,query(rs,a[j],i-1));
return ans+dfs(ls,i-1)+dfs(rs,i-1)+(1<<i);
}
}Trie;
signed main(){
n=iut(),Trie.BUILD(),Trie.L[1]=1,Trie.R[1]=n;
for (rr int i=1;i<=n;++i) a[i]=iut();
sort(a+1,a+1+n);
for (rr int i=1;i<=n;++i) Trie.Insert(a[i],i);
return !printf("%lld",Trie.dfs(1,29));
}