Boruvka最小生成树算法

在做 CF888G 的时候胡出来了这个东西,发现居然是个有名有姓的算法,震惊我一百年,故记之。

算法流程

引用某个课件(侵删)

CF 888G

和上面一样,只不过找最小值的时候用 Trie 来找,时间复杂度 \(\mathcal O(n\log n\log a_i+n\log^2 n)\)

当然也可以直接贪心。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
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<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
const int N=3e5+10;
typedef long long ll;
int f[N],a[N];
void init(int n){for(int i=1;i<=n;i++)f[i]=i;}
int getf(int x){return f[x]==x?x:f[x]=getf(f[x]);}
int trie[N*30][2],sz[N*30],ed[N*30],tot=0;
void ins(int x,int pos)
{
	int p=0;
	for(int i=29;i>=0;i--)
	{
		int op=(x&(1<<i))!=0;
		if(!trie[p][op])trie[p][op]=++tot;
		p=trie[p][op];
		sz[p]++;
	}
	ed[p]=pos;
}
void del(int x)
{
	int p=0;
	for(int i=29;i>=0;i--)
	{
		int op=(x&(1<<i))!=0;
		p=trie[p][op];
		sz[p]--;
	}
}
int query(int x)
{
	int p=0;
	for(int i=29;i>=0;i--)
	{
		int op=(x&(1<<i))!=0;
		if(trie[p][op]&&sz[trie[p][op]])p=trie[p][op];
		else p=trie[p][op^1];
	}
	return ed[p];
}
int n;ll ans=0; 
vector<int> v[N];
struct node
{
	int u,v,w;
	bool operator <(const node &x)const {return w<x.w;}
};
void sol()
{
	int cnt=0;
	for(int i=1;i<=n;i++)cnt+=getf(i)==i;
	if(cnt==1)return;
	for(int i=1;i<=n;i++)v[i].clear();
	for(int i=1;i<=n;i++)v[getf(i)].push_back(i);
	vector<node> s;
	for(int i=1;i<=n;i++)
	{
		if(v[i].empty())continue;
		int Min=0x7fffffff,pos=0,pos1=0;
		for(int j=0;j<v[i].size();j++)
		{
			int x=v[i][j];
			del(a[x]);
		}
		for(int j=0;j<v[i].size();j++)
		{
			int x=v[i][j],tmp=query(a[x]);
			if((a[x]^a[tmp])<Min)Min=(a[x]^a[tmp]),pos=x,pos1=tmp;
		}
		s.push_back((node){pos,pos1,Min});
		for(int j=0;j<v[i].size();j++)
		{
			int x=v[i][j];
			ins(a[x],x);
		}
	}
	sort(s.begin(),s.end());
	for(int i=0;i<s.size();i++)
	{
		int u=s[i].u,v=s[i].v,w=s[i].w;
		if(getf(u)!=getf(v))ans+=w,f[getf(v)]=getf(u);
	}
	sol();
}
signed main()
{
	n=read();init(n);
	for(int i=1;i<=n;i++)a[i]=read();
	sort(a+1,a+n+1);
	n=unique(a+1,a+n+1)-a-1;
	for(int i=1;i<=n;i++)ins(a[i],i);
	sol();
	printf("%lld",ans);
	return 0;
}
posted @ 2021-05-25 16:43  zzt1208  阅读(108)  评论(0编辑  收藏  举报