并查集
liruixiong0101 · · 个人记录
并查集 update on 2023/1/14 (\%100/\%100)
Tips:并查集真的真的不难
零、并查集的用法
众所周知并查集是一个很简单的东西
只需要用你的亿点点观察能力and注意力就so easy
并查集=并+查(合并+查询)
并查集往往用树来存,使用
查:
int fa[];//存储每个节点的父节点
int find(int x){
return fa[x] == x ? x : fa[x] = find(fa[x]);
//fa[x] = find(fa[x])路径压缩
//将所有节点的父节点变成这棵树的根节点
}
等等,让我们来感性理解一下
路径压缩:
每个节点find后都会指向这棵树的根节点,使每次find时,节点只需一次即可到达根节点
并:
int fa[];
int find(int x){}
//同上“查”
void merge(int x , int y){
int rx = find(x) , ry = find(y);
if(rx == ry) return;
//若根节点相同为同一集合不用合并
fa[ry] = rx;//fa[rx] = ry; 两者选一
//将两棵树的根节点连线
return;
}
让我们继续来感性理解一下
合并操作:
在主函数记得初始化:
int main(){
for(int i = 1; i <= n; i++) fa[i] = i;
//初始化
......
return 0;
}
一、朴素并查集(又称普通并查集或并查集)
照抄上述代码即可
P1551 亲戚
太简单了不想讲解了
代码如下:
int n , m , p , fa[];
int find(int x){}
void merge(int x , int y){}
//并查集
int main(){
cin >> n >> m >> p;
for(int i = 1; i <= n; i++) fa[i] = i;//初始化
while(m--){
int u , v;
cin >> u >> v;
//合并u,v
}
while(p--){
int u , v;
cin >> u >> v;
if(/*是亲戚*/) puts("Yes");
else puts("No");
}
return 0;
}
二、种类并查集
前面说的是朴素并查集(也可以是普通并查集
如:P1525 [NOIP2010 提高组] 关押罪犯有敌人和朋友两种关系,先按照
int n , m , f[];
int find(int x){}
void merge(int x , int y){}
//前面都一样
struct node{
int a , b , c;
}x[];
bool cmp(node x , node y){}//cmp函数我就不给了
int main(){
__________;
//输入+初始化
sort(x + 1 , x + 1 + m , cmp);//排序
for(int i = 1; i <= m; i++){
int a = x[i].a , b = x[i].b , c = x[i].c;
if(find(a) == find(b) || find(a + n) == find(b + n)){
cout << c;
return 0;
}//若为同一监狱直接输出c
merge(a , b + n);
merge(b , a + n);
//合并
}
cout << 0;//不会发生冲突
return 0;
}
三、带权并查集
带权并查集就是每个节点或都有一个或多个权值需要在合并or查询时维护这些权值即可
如P1196 [NOI2002] 银河英雄传说可以维护一个
具体代码如下:
int fa[] , pre[] , num[];
int find(int x){
if(fa[x] == x) return x;
int rx = find(fa[x]);
pre[x] += pre[fa[x]];
return fa[x] = rx;
}
void marge(int x , int y){
int rx = find(x) , ry = find(y);
pre[rx] += num[ry];
num[ry] += num[rx];
fa[rx] = ry;
num[rx] = 0;
return;
}
好了,并查集就是以上内容了
下期内容——搜索
本文图片均来自百度搜索