关于并查集的优化

并查集有两种优化方式:
第一个是路径压缩,这个保证每个点查询之后能够做到路径上节点O(1)查询代表元素.
但是路径压缩破坏了树形结构,所以这时我们找不到这个点是由哪个集合得来.
所以就有另一种优化方法,按秩合并并查集.
解释一下,秩就是并查集的深度.
我们每一次将深度较小者并到深度较大者,在二者深度相同时,整个并查集的秩增加.
这个时候就成功维护出了一个dep<=log n的并查集,且能够知道路径.
两者代码实现均不难,不加多余解释.
这里不再放路径压缩并查集例题了,之前整理过类似例题.
例题:按秩合并
这个题是可以将每个aibi连边,然后用按秩合并并查集解决.
因为一个边的两端节点只能选一个,所以维护一个size与edge数.
这两者取min为一个联通图可以贡献的答案.
用dep实现按秩合并,保证查询的深度不炸,然后每次将一个新点对应值并入集合,dfs后撤销操作.
每个点加入后统计答案即可.
update: 按秩合并可以直接用size来实现,这个也是满足深度性质的,自己手搓一下就知道了。

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define qr qr()
#define ps push_back
#define ve vector<int>
using namespace std;
const int N=2e5+200;
int n,m,a[N],b[N],f[N],sz[N],ed[N],dep[N],ans[N];
ve e[N];
inline ll qr{
	ll x=0;char ch=getchar();bool f=0;
	while(ch>57||ch<48)f=(ch=='-')?1:0,ch=getchar();
	while(ch>=48&&ch<=57)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
int find(int now){
	return (f[now]==now)?now:find(f[now]);
}
void dfs(int now,int fa){
	// cout<<now<<' '<<fa<<endl;
	int x=find(a[now]),y=find(b[now]);
	if(dep[x]<dep[y])swap(x,y);
	ans[now]=ans[fa];
	ans[now]-=min(sz[x],ed[x]);
	int tmp=dep[x];
	if(x^y){
		ans[now]-=min(sz[y],ed[y]);
		sz[x]+=sz[y];
		ed[x]+=ed[y];
		f[y]=x;
		dep[x]+=(dep[x]==dep[y]);
	}
	++ed[x];
	ans[now]+=min(ed[x],sz[x]);
	for(auto to:e[now])
		if(to^fa)dfs(to,now);
	--ed[x];
	if(x^y){
		sz[x]-=sz[y];
		ed[x]-=ed[y];
		f[y]=y;
		dep[x]=tmp;
	}
}
void init(){
	n=qr;
	for(int i=1;i<=n;++i){
		a[i]=qr,b[i]=qr;
		f[i]=i;sz[i]=1;dep[i]=1;
	}
	int f,t;
	for(int i=1;i<n;++i){
		f=qr,t=qr;
		e[f].ps(t);e[t].ps(f);
	}dfs(1,0);
	for(int i=2;i<=n;++i)
		printf("%d ",ans[i]);
}
int main(){
	




	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);





	// ios::sync_with_stdio(0);
	// cin.tie(0);
	// cout.tie(0);
	init();
	return 0;
}
posted @   SLS-wwppcc  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示