【笔记】并查集

描述:并查集是一种对集合进行合并、查询等一系列操作。时间复杂度为O(a(n)) 比O(logn)还快。

代码

 1 int fa[sz];//集合数组
 2 void init()//预处理
 3 {
 4     for(int i=1;i<=n;i++)
 5     {
 6         fa[i]=i;//初始时每个点都是一个独立的集合
 7         rank[i]=0;//按秩合并 初始时每一个集合形成的树的高度为0;
 8     }
 9 }
10 int find(int x)//查询操作 返回x所在集合的代表元素
11 {
12     return fa[x]==x?x:fa[x]=find(fa[x]);
13     //如果x就是它所在集合的代表元素 就返回x
14     //反之 让它到代表元素上的点的父节点都变成代表元素 并返回修改后父节点的值 即代表元素
15     //路径压缩 使并查集更加高效
16 }
17 void unite(int u,int v)//合并操作
18 {
19     u=find(u),v=find(v);//对u和v的集合进行合并操作 因此将它们变成其所在集合代表元素
20     if(u==v) return ;//如果u和v在同一集合内 则不必进行合并操作
21     //按秩合并 防止树退化成链
22     if(rank[u]<rank[v])//如果u和v所在子树的rank不同
23     {
24         fa[u]=v;//就将rank小的合并到rank大的上
25     }
26     else
27     {
28         fa[v]=u;
29         if(rank[u]==rank[v])
30             rank[u]++;
31     }
32 }
33 bool same(int u,int v)//判断两个元素是否属于同一集合
34 {
35     u=find(u),v=find(v);
36     return u==v;
37 }

拓展及应用

①拓展域并查集

例题:

1.洛谷P1892 团伙

思路:每个节点维护两个域--朋友域fa[x]和敌人域fa[x+n],若u与v是敌人,u的敌人域等于v的朋友域,遂合并。

若u与v是朋友,就将两者的朋友域进行合并。最后看每个节点属于多少个不同的集合即为答案。

AC Code:

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int sz=1000+100;
 5 int fa[sz<<1];
 6 int find(int x)
 7 {
 8     return fa[x]==x?x:fa[x]=find(fa[x]);
 9 }
10 bool vis[sz<<1];
11 int main()
12 {
13     int n,m;
14     scanf("%d%d",&n,&m);
15     char c;int u,v;
16     for(int i=1;i<=(n<<1);i++)
17     {
18         fa[i]=i;
19     }
20     for(int i=1;i<=m;i++)
21     {
22         cin>>c>>u>>v;
23         if(c==‘F‘)
24         {
25             fa[find(u)]=find(v);
26         }
27         if(c==‘E‘)
28         {
29             fa[find(v)]=find(u+n);
30             fa[find(v+n)]=find(u);
31         }
32     }
33     for(int i=1;i<=n;i++)
34         vis[find(i)]=true;
35     int ans=0;
36     for(int i=1;i<=(n<<1);i++)
37         if(vis[i]) ans++;
38     printf("%d",ans);
39     return 0;
40 }

2.P2024 食物链

思路:与上面的题目类似,对每个动物维护三个域:同类域fa[x],吃域fa[x+n],被吃域fa[x+n*2]。

若x与y是同类,就将它们的三个域分别合并;若x被y吃,则x的同类域等于y的吃域,x的吃域等于y的被吃域,x的被吃域等于y的同类域。

AC Code:

#include<cstdio>
using namespace std;
const int sz=300000+500;
int fa[sz];
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=3*k;i++)
        fa[i]=i;
    int num,x,y,ans=0;
    for(int i=1;i<=k;i++)
    {
        scanf("%d%d%d",&num,&x,&y);
        if(x>n||y>n)//若x或y超过了范围,则为假话
        {
            ans++;continue;
        }
        int x1=find(x),x2=find(x+n),x3=find(x+2*n);//x1=x的同类域 x2=x的吃域 x3=x的被吃域
        int y1=find(y),y2=find(y+n),y3=find(y+2*n);
        if(num==1)//若x与y是同类
        {
            if(x1==y2||x2==y1) ans++;//若存在吃与被吃关系 则为假话
            else
            {
                fa[x1]=y1;fa[x2]=y2;fa[x3]=y3;//若不矛盾 则为真话 合并集合
            }
        }
        if(num==2)//若x吃y
        {
            if(x1==y1||x1==y2) ans++;//若x与y是同类 或x被y吃 则为假话
            else
            {
                fa[x1]=y3;fa[x2]=y1;fa[x3]=y2;//反之,x的同类域等于y的吃域,x的吃域等于y的被吃域,x的被吃域等于y的同类域
            }
        }
    }
    printf("%d",ans);
    return 0;
}

②带权并查集

学习ing 以后再填。。。

①最小生成树 MST

并查集是Kruskal的灵魂。

例题:

P2330 [SCOI2005]繁忙的都市

思路:Kruskal大家应该都会吧....

AC Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int sz=50000+50;
 6 int n,m;
 7 struct node{
 8     int x,y,w;
 9 }e[sz*2];
10 bool cmp(node a,node b)
11 {
12     return a.w<b.w;
13 }
14 int fa[sz];
15 int find(int x)
16 {
17     return fa[x]==x?x:fa[x]=find(fa[x]);
18 }
19 int ans;
20 void kruskal()
21 {
22     ans=-999;
23     for(int i=1;i<=m;i++)
24     {
25         int x=e[i].x,y=e[i].y;
26         if(find(x)!=find(y))
27         {
28             fa[find(x)]=find(y);
29             ans=max(ans,e[i].w);
30         }
31     }
32 }
33 int main()
34 {
35     scanf("%d%d",&n,&m);
36     for(int i=1;i<=m;i++)
37     {
38         int u,v,w;
39         scanf("%d%d%d",&u,&v,&w);
40         e[i].x=u,e[i].y=v,e[i].w=w;
41     }
42     sort(e+1,e+m+1,cmp);
43     for(int i=1;i<=n;i++)
44         fa[i]=i;
45     kruskal();
46     printf("%d %d",n-1,ans);
47     return 0;
48 }

②求最小环

.

例题:P2661 信息传递

思路:这道题可以DFS过,也可以用并查集做

AC Code:

③求满足要求的集合个数

看有多少x的fa[x]=x。

例题:P3420 [POI2005]SKA-Piggy Banks

思路:将钥匙相同的存钱罐连成一个大存钱罐,最后看大存钱罐的数量。

AC Code:

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int sz=1000000+100;
 5 int fa[sz];
 6 int find(int x)
 7 {
 8     return fa[x]==x?x:fa[x]=find(fa[x]);
 9 }
10 int main()
11 {
12     int n;
13     scanf("%d",&n);
14     for(int i=1;i<=n;i++)
15         fa[i]=i;
16     for(int i=1;i<=n;i++)
17     {
18         int k;
19         scanf("%d",&k);
20         int u=find(i),v=find(k);
21         fa[u]=v;
22     }
23     int cnt=0;
24     for(int i=1;i<=n;i++)
25         if(fa[i]==i)
26             cnt++;
27     printf("%d",cnt);
28     return 0;
29 }

④求LCA

End.

时间: 2024-11-05 20:26:42

【笔记】并查集的相关文章

读书笔记 之 数据结构(并查集详解)(POJ1703)

<ACM/ICPC算法训练教程>读书笔记-这一次补上并查集的部分.将对并查集的思想进行详细阐述,并附上本人AC掉POJ1703的Code. 在一些有N个元素的集合应用问题中,通常会将每个元素构成单元素集合,然后按照一定顺序将同属一组的集合合并,期间要反复查找每一个元素在哪个集合中.这类问题往往看似简单,但是数据量很大,因此容易造成TLE或MLE,也就是空间度和时间度极其复杂.因此在这里,我们引入一种抽象的特殊数据结构——并查集. 并查集:类似一个族谱,每个结点均有一个father[x]来表示x

【算法学习笔记】41.并查集 SJTU OJ 1283 Mixture

---恢复内容开始--- Description CC非常喜欢化学,并且特别喜欢把一大堆液体倒在一起. 现在CC有n种液体,其中m对会发生反应,现在她想把这n种液体按某种顺序倒入一个容器内,让她获得最刺激的体验,使危险系数尽量大. 我们可以这样计算危险系数,一开始容器内没有任何液体,危险系数为1.每次液体倒入容器时,若容器内已有一种或多种液体会与这种液体发生反应,则危险系数会乘2,否则危险系数不变. 请你求出把这n种液体倒在一起的最大危险系数. Input Format 第一行为两个数n和m.

数据结构学习笔记04树(堆 哈夫曼树 并查集)

一.堆(heap) 优先队列(Priority Queue):特殊的“队列”,取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序. 数组 : 插入 — 元素总是插入尾部 ~ O ( 1 ) 删除 — 查找最大(或最小)关键字 ~ O ( n ) 从数组中删去需要移动元素 ~ O( n ) 链表: 插入 — 元素总是插入链表的头部 ~ O ( 1 ) 删除 — 查找最大(或最小)关键字 ~ O ( n ) 删去结点 ~ O( 1 ) 有序数组: 插入 — 找到合适的位置

【算法学习笔记】44. 并查集补充 SJTU OJ 3015 露子的星空

[题目描述] 已经深夜了,露子仍然在公园里仰望星空.你走近后,她对你说:“呜—,你看夜空中的星星.它们本来都是孤独地毫无联系,但人们赋予了它们各种地联想,在它们之间连上了线,便形成了夜空中无数的星座.”你回答:“是啊.为什么我们不自己创造一个美丽的星空呢?” 假设夜空中一共有n颗星星,它们初始时都没有连线.接下来,露子会给你m条指令.一共有以下三种指令: 1.在某两颗星星间连线.(可能会重复连接两颗星星) 2.询问你当前夜空中一共有多少个星座. 3.某两颗星星当前是否属于同一个星座. 其中星座是

并查集 (Union-Find Sets)及其应用

定义 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示. 集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并. 主要操作 初始化 把每个点所在集合初始化为其自身. 通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N). 查找 查找元素所在的集合,即根节点. 合并 将两个元素所在的集合合并为一个集合. 通常来说,合并之前,应先判断两个元素是否属于

并查集详解 (转)

http://blog.csdn.net/dellaserss/article/details/7724401 我从CSDN转的文章,原文作者我也不懂是谁,文章写得真的是诙谐幽默,使得内容更容易理解了. 来看一个实例,杭电OJ 1232畅通工程 Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可).问最少还需要建

并查集--连通图相关

早上一番捣鼓,把以前丢失的onenote笔记找出来一部分. 看到并查集,大二做的笔记,现在已经毫无印象了 记得当时看的时候挺费劲,云里雾里的 现在再看一遍竟然毫无压力,一次读懂 其实确实挺简单的,没有那么高深.可能当时玩acm的时候太没自信了,看啥都难... 核心思想是用一个节点代表一块连通分支 可以通过路径压缩来减少以后查找的时间 非路径压缩递归写法: int fFind(int i) { if(pre[i]!=i) pre[i]=fFind(pre[i]); return pre[i]; }

CCIE学习笔记之特性集

etherchannel loadbalance  dst –mac 以太网隧道负载方式基于目标MAC MST (config)# spanning -tree mode mst (config)# spanning -tree mst configuration (config-mst )#  name CCIE (config-mst )#  instance 1 vlan 20,40,60 spanning -tree mst 3 root primary spanning -tree m

CodeForces 745C Hongcow Builds A Nation 并查集

题意: 给了你n个城市 m条边 k个政府 每个政府管辖的区域内不能和其他政府的区域有相连 即政府之间不存在路径 问你在维护这种关系的同时 最多再加多少条边 思路: 先找出来每个联通块 再找出来没有归属的孤立的点 把他们都放到最大的联通块里 然后每个联通块之间的点两两连边是n*(n-1)/2条边 最后算出来的ans-m就好了 (看别人的博客学了一个max_element 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a