Borůvka

Borůvka

\(O(m\log n)\)\(MST\),一般用于求完全图

\(Borůvka\) 其实是一种多路增广的 \(prim\)

\(Prim\) 算法由一个点开始,往外不断贪心地找最短边,然后不断扩大连通块,直到形成一棵树

\(Borůvka\) 算法每一次的增广,会对现在的每一个连通块都找一遍的最短边,最后每个连通块择优,将这些边全部连上

  1. 对于现在的每个连通块,找到从这个连通块出发,不在最小生成树中的、到达别的连通块的最短边

(注:若权值相同,则需要再按照另一个维度严格排序,常用标号大小排序。即边权相同时,认为编号小的边短。这样处理是为了避免两个连通块互相连的时候出现环)

  1. 全部找完后,将这些边加入最小生成树中,并查集合并

每次合并 \(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)\)

posted @ 2023-07-17 16:06  xyzfrozen  阅读(14)  评论(0)    收藏  举报