【CF888G】Xor-MST(最小生成树,Trie树)

【CF888G】Xor-MST(最小生成树,Trie树)

题面

CF
洛谷

题解

利用\(Kruskal\)或者\(Prim\)算法都很不好计算。
然而我们还有一个叫啥来着?\(B\)啥啥的算法,就叫\(B\)算法吧。
思想是对于每个点找到一条最小边,并且将这条边连上,不难证明每次至少连上了\(n/2\)个点。
再将这些联通块看做一个点继续重复这个过程,时间复杂度是\(log\)级别的。

我们从高位往低位看,如果我们按照\(01\)分类,根据上述的过程,不难得到,如果\(01\)两个集合都存在的话,它们之间一定会连上一条边,并且\(01\)分类后的两个集合一定在内部形成联通块,这就很好办了,直接分治递归处理,每次拿\(Trie\)算一下\(01\)分类后两个集合之间边的最小值然后给连上就好。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 200200
#define pb push_back
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,a[MAX],tot;
struct Node{int ch[2];}t[MAX<<5];
void insert(int &x,int w,int p)
{
	if(!x)x=++tot,t[x].ch[0]=t[x].ch[1]=0;
	if(p==-1)return;
	insert(t[x].ch[(w>>p)&1],w,p-1);
}
int Query(int x,int w,int p)
{
	if(p==-1)return 0;int c=(w>>p)&1;
	if(t[x].ch[c])return Query(t[x].ch[c],w,p-1);
	else return Query(t[x].ch[c^1],w,p-1)^(1<<p);
}
ll Solve(vector<int> v,int p)
{
	if(!v.size()||p==-1)return 0;
	vector<int> d[2];int ret=0,rt;
	for(int i:v)d[(i>>p)&1].pb(i);
	if(d[0].size()&&d[1].size())
	{
		ret=1<<(p+1);rt=tot=0;
		for(int i:d[0])insert(rt,i,30);
		for(int i:d[1])ret=min(ret,Query(rt,i,30));
	}
	return ret+Solve(d[0],p-1)+Solve(d[1],p-1);
}
int main()
{
	n=read();vector<int> a;
	for(int i=1;i<=n;++i)a.pb(read());
	printf("%I64d\n",Solve(a,30));
	return 0;
}

posted @ 2018-08-15 09:39  小蒟蒻yyb  阅读(906)  评论(0编辑  收藏  举报