Borůvka
Borůvka
\(O(m\log n)\) 求 \(MST\),一般用于求完全图
\(Borůvka\) 其实是一种多路增广的 \(prim\)
\(Prim\) 算法由一个点开始,往外不断贪心地找最短边,然后不断扩大连通块,直到形成一棵树
而 \(Borůvka\) 算法每一次的增广,会对现在的每一个连通块都找一遍的最短边,最后每个连通块择优,将这些边全部连上
- 对于现在的每个连通块,找到从这个连通块出发,不在最小生成树中的、到达别的连通块的最短边
(注:若权值相同,则需要再按照另一个维度严格排序,常用标号大小排序。即边权相同时,认为编号小的边短。这样处理是为了避免两个连通块互相连的时候出现环)
- 全部找完后,将这些边加入最小生成树中,并查集合并
每次合并 \(O(m)\),连通块个数至少减半,所以最多合并 \(O(\log n)\) 次,重点就在第一步,复杂度是 \(O(T\log n)\) 的,\(O(T)\) 为第一步复杂度,这里就是 \(O(m)\)
模板
const int N=5e3+10,M=2e5+10;
int n,m,u,v,w[M];
int p[N],cur[N];
bool used[M];
struct node{int u,v;}ed[M];
void Bka()
{
for(int i=1;i<=n;i++) p[i]=i;
int res=0,cnt=0;
bool fl=1;
while(fl)
{
memset(cur,0,sizeof cur);
fl=0;
for(int i=1;i<=m;i++)
{
if(used[i]) continue;
int a=ed[i].u,b=ed[i].v;
int x=find(a),y=find(b);
if(x!=y)
{
if(!cur[x] || w[cur[x]]>w[i]) cur[x]=i;
if(!cur[y] || w[cur[y]]>w[i]) cur[y]=i;
}
}
for(int i=1;i<=n;i++)
{
if(cur[i] && !used[cur[i]])
{
int a=ed[cur[i]].u,b=ed[cur[i]].v;
int x=find(a),y=find(b);
if(x!=y)
{
fl=1;
cnt++,res+=w[cur[i]];
p[x]=y;
used[cur[i]]=1;
}
}
}
if(cnt==n-1) break;
}
if(cnt==n-1) fw(res);
else puts("orz");
}
int main()
{
n=fr(),m=fr();
for(int i=1;i<=m;i++)
{
u=fr(),v=fr(),w[i]=fr();
ed[i]={u,v};
}
Bka();
return 0;
}
problem
CF Xor-MST
考虑 \(Boruvka\)
每次对于一个连通块找到一个连通块外的异或最小的一个数,考虑用 \(Trie\) 维护和查询,维护一个所有数构成的 \(Trie\),以及每个连通块一个 Trie
每次查询连通块 \(S\) 的答案时,把所有 \(a_i \in S\) 从整颗 \(Trie\) 里面删去,然后枚举每个 \(a_i \in S\) 查询即可
合并时暴力合并两个 Trie,注意到总节点个数最多为 \(n\log n\) ,合并时每到一个节点即删去一个节点,因此是均摊 \(O(n\log n)\) 的
求第一步是 \(O(n\log a)\) 的,总复杂度 \(O(n\log a\log n)\)