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;
}