#最小生成树,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));
} 
posted @ 2021-03-05 21:56  lemondinosaur  阅读(51)  评论(0编辑  收藏  举报